initial commit supporting converting corefx to using grpc from previous rest client
This commit is contained in:
parent
f283c747cc
commit
a593289f1b
147 changed files with 10372 additions and 0 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -55,3 +55,6 @@ coverage.xml
|
||||||
netns/setup.py
|
netns/setup.py
|
||||||
daemon/setup.py
|
daemon/setup.py
|
||||||
ns3/setup.py
|
ns3/setup.py
|
||||||
|
|
||||||
|
# ignore corefx build
|
||||||
|
corefx/target
|
||||||
|
|
156
corefx/pom.xml
Normal file
156
corefx/pom.xml
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>com.core</groupId>
|
||||||
|
<artifactId>corefx</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<maven.compiler.source>1.8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>1.8</maven.compiler.target>
|
||||||
|
<jung.version>2.1.1</jung.version>
|
||||||
|
<jackson.version>2.9.6</jackson.version>
|
||||||
|
<grpc.version>1.20.0</grpc.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.sf.jung</groupId>
|
||||||
|
<artifactId>jung-api</artifactId>
|
||||||
|
<version>${jung.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.sf.jung</groupId>
|
||||||
|
<artifactId>jung-graph-impl</artifactId>
|
||||||
|
<version>${jung.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.sf.jung</groupId>
|
||||||
|
<artifactId>jung-algorithms</artifactId>
|
||||||
|
<version>${jung.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.sf.jung</groupId>
|
||||||
|
<artifactId>jung-io</artifactId>
|
||||||
|
<version>${jung.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.sf.jung</groupId>
|
||||||
|
<artifactId>jung-visualization</artifactId>
|
||||||
|
<version>${jung.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-core</artifactId>
|
||||||
|
<version>${jackson.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
<version>${jackson.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-annotations</artifactId>
|
||||||
|
<version>${jackson.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.squareup.okhttp3</groupId>
|
||||||
|
<artifactId>okhttp</artifactId>
|
||||||
|
<version>3.11.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-api</artifactId>
|
||||||
|
<version>2.9.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-core</artifactId>
|
||||||
|
<version>2.9.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.socket</groupId>
|
||||||
|
<artifactId>socket.io-client</artifactId>
|
||||||
|
<version>0.8.3</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>1.18.0</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-net</groupId>
|
||||||
|
<artifactId>commons-net</artifactId>
|
||||||
|
<version>3.6</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.jfoenix</groupId>
|
||||||
|
<artifactId>jfoenix</artifactId>
|
||||||
|
<version>8.0.7</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.grpc</groupId>
|
||||||
|
<artifactId>grpc-netty-shaded</artifactId>
|
||||||
|
<version>${grpc.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.grpc</groupId>
|
||||||
|
<artifactId>grpc-protobuf</artifactId>
|
||||||
|
<version>${grpc.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.grpc</groupId>
|
||||||
|
<artifactId>grpc-stub</artifactId>
|
||||||
|
<version>${grpc.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.guava</groupId>
|
||||||
|
<artifactId>guava</artifactId>
|
||||||
|
<version>20.0</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<extensions>
|
||||||
|
<extension>
|
||||||
|
<groupId>kr.motd.maven</groupId>
|
||||||
|
<artifactId>os-maven-plugin</artifactId>
|
||||||
|
<version>1.5.0.Final</version>
|
||||||
|
</extension>
|
||||||
|
</extensions>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>com.zenjava</groupId>
|
||||||
|
<artifactId>javafx-maven-plugin</artifactId>
|
||||||
|
<version>8.8.3</version>
|
||||||
|
<configuration>
|
||||||
|
<mainClass>com.core.Main</mainClass>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.xolstice.maven.plugins</groupId>
|
||||||
|
<artifactId>protobuf-maven-plugin</artifactId>
|
||||||
|
<version>0.5.1</version>
|
||||||
|
<configuration>
|
||||||
|
<protocArtifact>com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier}</protocArtifact>
|
||||||
|
<pluginId>grpc-java</pluginId>
|
||||||
|
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>compile</goal>
|
||||||
|
<goal>compile-custom</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
543
corefx/src/main/java/com/core/Controller.java
Normal file
543
corefx/src/main/java/com/core/Controller.java
Normal file
|
@ -0,0 +1,543 @@
|
||||||
|
package com.core;
|
||||||
|
|
||||||
|
import com.core.client.ICoreClient;
|
||||||
|
import com.core.client.grpc.CoreGrpcClient;
|
||||||
|
import com.core.data.*;
|
||||||
|
import com.core.graph.NetworkGraph;
|
||||||
|
import com.core.ui.*;
|
||||||
|
import com.core.ui.dialogs.*;
|
||||||
|
import com.core.utils.ConfigUtils;
|
||||||
|
import com.core.utils.Configuration;
|
||||||
|
import com.core.utils.NodeTypeConfig;
|
||||||
|
import com.core.websocket.CoreWebSocket;
|
||||||
|
import com.jfoenix.controls.JFXDecorator;
|
||||||
|
import com.jfoenix.controls.JFXProgressBar;
|
||||||
|
import javafx.application.Application;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.concurrent.Task;
|
||||||
|
import javafx.embed.swing.SwingNode;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.fxml.Initializable;
|
||||||
|
import javafx.scene.control.CheckMenuItem;
|
||||||
|
import javafx.scene.control.MenuItem;
|
||||||
|
import javafx.scene.layout.BorderPane;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.stage.FileChooser;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.awt.event.ItemEvent;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class Controller implements Initializable {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
@FXML private StackPane stackPane;
|
||||||
|
@FXML private BorderPane borderPane;
|
||||||
|
@FXML private VBox top;
|
||||||
|
@FXML private VBox bottom;
|
||||||
|
@FXML private SwingNode swingNode;
|
||||||
|
@FXML private MenuItem saveXmlMenuItem;
|
||||||
|
@FXML private JFXProgressBar progressBar;
|
||||||
|
@FXML private CheckMenuItem throughputMenuItem;
|
||||||
|
|
||||||
|
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
|
||||||
|
private final Map<Integer, MobilityConfig> mobilityScripts = new HashMap<>();
|
||||||
|
private final Map<Integer, MobilityPlayerDialog> mobilityPlayerDialogs = new HashMap<>();
|
||||||
|
private Application application;
|
||||||
|
private JFXDecorator decorator;
|
||||||
|
private Stage window;
|
||||||
|
private Configuration configuration;
|
||||||
|
private Map<String, Set<String>> defaultServices = new HashMap<>();
|
||||||
|
|
||||||
|
// core client utilities
|
||||||
|
private ICoreClient coreClient = new CoreGrpcClient();
|
||||||
|
private CoreWebSocket coreWebSocket;
|
||||||
|
|
||||||
|
// ui elements
|
||||||
|
private NetworkGraph networkGraph = new NetworkGraph(this);
|
||||||
|
private AnnotationToolbar annotationToolbar = new AnnotationToolbar(networkGraph);
|
||||||
|
private NodeDetails nodeDetails = new NodeDetails(this);
|
||||||
|
private LinkDetails linkDetails = new LinkDetails(this);
|
||||||
|
private GraphToolbar graphToolbar = new GraphToolbar(this);
|
||||||
|
|
||||||
|
// dialogs
|
||||||
|
private SessionsDialog sessionsDialog = new SessionsDialog(this);
|
||||||
|
private ServiceDialog serviceDialog = new ServiceDialog(this);
|
||||||
|
private NodeServicesDialog nodeServicesDialog = new NodeServicesDialog(this);
|
||||||
|
private NodeEmaneDialog nodeEmaneDialog = new NodeEmaneDialog(this);
|
||||||
|
private NodeWlanDialog nodeWlanDialog = new NodeWlanDialog(this);
|
||||||
|
private ConfigDialog configDialog = new ConfigDialog(this);
|
||||||
|
private HooksDialog hooksDialog = new HooksDialog(this);
|
||||||
|
private MobilityDialog mobilityDialog = new MobilityDialog(this);
|
||||||
|
private ChartDialog chartDialog = new ChartDialog(this);
|
||||||
|
private NodeTypesDialog nodeTypesDialog = new NodeTypesDialog(this);
|
||||||
|
private BackgroundDialog backgroundDialog = new BackgroundDialog(this);
|
||||||
|
private LocationDialog locationDialog = new LocationDialog(this);
|
||||||
|
private GeoDialog geoDialog = new GeoDialog(this);
|
||||||
|
private ConnectDialog connectDialog = new ConnectDialog(this);
|
||||||
|
private GuiPreferencesDialog guiPreferencesDialog = new GuiPreferencesDialog(this);
|
||||||
|
private NodeTypeCreateDialog nodeTypeCreateDialog = new NodeTypeCreateDialog(this);
|
||||||
|
|
||||||
|
public void connectToCore(String address, int port) {
|
||||||
|
coreWebSocket.stop();
|
||||||
|
|
||||||
|
ExecutorService executorService = Executors.newSingleThreadExecutor();
|
||||||
|
executorService.submit(() -> {
|
||||||
|
try {
|
||||||
|
coreWebSocket.start(address, port);
|
||||||
|
coreClient.setConnection(address, port);
|
||||||
|
initialJoin();
|
||||||
|
} catch (IOException | URISyntaxException ex) {
|
||||||
|
Toast.error(String.format("Connection failure: %s", ex.getMessage()), ex);
|
||||||
|
Platform.runLater(() -> connectDialog.showDialog());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initialJoin() throws IOException {
|
||||||
|
Map<String, List<String>> serviceGroups = coreClient.getServices();
|
||||||
|
logger.info("core services: {}", serviceGroups);
|
||||||
|
nodeServicesDialog.setServices(serviceGroups);
|
||||||
|
nodeTypeCreateDialog.setServices(serviceGroups);
|
||||||
|
|
||||||
|
logger.info("initial core session join");
|
||||||
|
List<SessionOverview> sessions = coreClient.getSessions();
|
||||||
|
|
||||||
|
logger.info("existing sessions: {}", sessions);
|
||||||
|
Integer sessionId;
|
||||||
|
if (sessions.isEmpty()) {
|
||||||
|
logger.info("creating initial session");
|
||||||
|
SessionOverview sessionOverview = coreClient.createSession();
|
||||||
|
sessionId = sessionOverview.getId();
|
||||||
|
Toast.info(String.format("Created Session %s", sessionId));
|
||||||
|
} else {
|
||||||
|
SessionOverview sessionOverview = sessions.get(0);
|
||||||
|
sessionId = sessionOverview.getId();
|
||||||
|
Toast.info(String.format("Joined Session %s", sessionId));
|
||||||
|
}
|
||||||
|
|
||||||
|
joinSession(sessionId);
|
||||||
|
|
||||||
|
// set emane models
|
||||||
|
List<String> emaneModels = coreClient.getEmaneModels();
|
||||||
|
nodeEmaneDialog.setModels(emaneModels);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void joinSession(Integer sessionId) throws IOException {
|
||||||
|
// clear graph
|
||||||
|
networkGraph.reset();
|
||||||
|
|
||||||
|
// clear out any previously set information
|
||||||
|
mobilityPlayerDialogs.clear();
|
||||||
|
mobilityScripts.clear();
|
||||||
|
mobilityDialog.setNode(null);
|
||||||
|
Platform.runLater(() -> borderPane.setRight(null));
|
||||||
|
|
||||||
|
// get session to join
|
||||||
|
Session session = coreClient.getSession(sessionId);
|
||||||
|
SessionState sessionState = SessionState.get(session.getState());
|
||||||
|
|
||||||
|
// update client to use this session
|
||||||
|
coreClient.updateSession(sessionId);
|
||||||
|
coreClient.updateState(sessionState);
|
||||||
|
|
||||||
|
// display all nodes
|
||||||
|
logger.info("joining core session({}) state({}): {}", sessionId, sessionState, session);
|
||||||
|
for (CoreNode node : session.getNodes()) {
|
||||||
|
NodeType nodeType = NodeType.find(node.getType(), node.getModel());
|
||||||
|
if (nodeType == null) {
|
||||||
|
logger.info(String.format("failed to find node type(%s) model(%s): %s",
|
||||||
|
node.getType(), node.getModel(), node.getName()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
node.setNodeType(nodeType);
|
||||||
|
networkGraph.addNode(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// display all links
|
||||||
|
for (CoreLink link : session.getLinks()) {
|
||||||
|
if (link.getInterfaceOne() != null || link.getInterfaceTwo() != null) {
|
||||||
|
link.setType(LinkTypes.WIRED.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
networkGraph.addLink(link);
|
||||||
|
}
|
||||||
|
|
||||||
|
// refresh graph
|
||||||
|
networkGraph.getGraphViewer().repaint();
|
||||||
|
|
||||||
|
// update other components for new session
|
||||||
|
graphToolbar.setRunButton(coreClient.isRunning());
|
||||||
|
hooksDialog.updateHooks();
|
||||||
|
|
||||||
|
// update session default services
|
||||||
|
setCoreDefaultServices();
|
||||||
|
|
||||||
|
// retrieve current mobility script configurations and show dialogs
|
||||||
|
Map<Integer, MobilityConfig> mobilityConfigMap = coreClient.getMobilityConfigs();
|
||||||
|
mobilityScripts.putAll(mobilityConfigMap);
|
||||||
|
showMobilityScriptDialogs();
|
||||||
|
|
||||||
|
Platform.runLater(() -> decorator.setTitle(String.format("CORE (Session %s)", sessionId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean startSession() throws IOException {
|
||||||
|
// force nodes to get latest positions
|
||||||
|
networkGraph.updatePositions();
|
||||||
|
|
||||||
|
// retrieve items for creation/start
|
||||||
|
Collection<CoreNode> nodes = networkGraph.getGraph().getVertices();
|
||||||
|
Collection<CoreLink> links = networkGraph.getGraph().getEdges();
|
||||||
|
List<Hook> hooks = hooksDialog.getHooks();
|
||||||
|
|
||||||
|
// start/create session
|
||||||
|
progressBar.setVisible(true);
|
||||||
|
boolean result = coreClient.start(nodes, links, hooks);
|
||||||
|
progressBar.setVisible(false);
|
||||||
|
if (result) {
|
||||||
|
showMobilityScriptDialogs();
|
||||||
|
saveXmlMenuItem.setDisable(false);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean stopSession() throws IOException {
|
||||||
|
// clear out any drawn wireless links
|
||||||
|
List<CoreLink> wirelessLinks = networkGraph.getGraph().getEdges().stream()
|
||||||
|
.filter(CoreLink::isWireless)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
wirelessLinks.forEach(networkGraph::removeWirelessLink);
|
||||||
|
networkGraph.getGraphViewer().repaint();
|
||||||
|
|
||||||
|
// stop session
|
||||||
|
progressBar.setVisible(true);
|
||||||
|
boolean result = coreClient.stop();
|
||||||
|
progressBar.setVisible(false);
|
||||||
|
if (result) {
|
||||||
|
saveXmlMenuItem.setDisable(true);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleThroughputs(Throughputs throughputs) {
|
||||||
|
for (InterfaceThroughput interfaceThroughput : throughputs.getInterfaces()) {
|
||||||
|
int nodeId = interfaceThroughput.getNode();
|
||||||
|
CoreNode node = networkGraph.getVertex(nodeId);
|
||||||
|
Collection<CoreLink> links = networkGraph.getGraph().getIncidentEdges(node);
|
||||||
|
int interfaceId = interfaceThroughput.getNodeInterface();
|
||||||
|
for (CoreLink link : links) {
|
||||||
|
if (nodeId == link.getNodeOne()) {
|
||||||
|
if (interfaceId == link.getInterfaceOne().getId()) {
|
||||||
|
link.setThroughput(interfaceThroughput.getThroughput());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (interfaceId == link.getInterfaceTwo().getId()) {
|
||||||
|
link.setThroughput(interfaceThroughput.getThroughput());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
networkGraph.getGraphViewer().repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setCoreDefaultServices() {
|
||||||
|
try {
|
||||||
|
coreClient.setDefaultServices(defaultServices);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Toast.error("Error updating core default services", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateNodeTypes() {
|
||||||
|
graphToolbar.setupNodeTypes();
|
||||||
|
setCoreDefaultServices();
|
||||||
|
try {
|
||||||
|
ConfigUtils.save(configuration);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Toast.error("Error saving configuration", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteNode(CoreNode node) {
|
||||||
|
networkGraph.removeNode(node);
|
||||||
|
CoreNode mobilityNode = mobilityDialog.getNode();
|
||||||
|
if (mobilityNode != null && mobilityNode.getId().equals(node.getId())) {
|
||||||
|
mobilityDialog.setNode(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setWindow(Stage window) {
|
||||||
|
this.window = window;
|
||||||
|
sessionsDialog.setOwner(window);
|
||||||
|
hooksDialog.setOwner(window);
|
||||||
|
nodeServicesDialog.setOwner(window);
|
||||||
|
serviceDialog.setOwner(window);
|
||||||
|
nodeWlanDialog.setOwner(window);
|
||||||
|
nodeEmaneDialog.setOwner(window);
|
||||||
|
configDialog.setOwner(window);
|
||||||
|
mobilityDialog.setOwner(window);
|
||||||
|
nodeTypesDialog.setOwner(window);
|
||||||
|
backgroundDialog.setOwner(window);
|
||||||
|
locationDialog.setOwner(window);
|
||||||
|
connectDialog.setOwner(window);
|
||||||
|
guiPreferencesDialog.setOwner(window);
|
||||||
|
nodeTypeCreateDialog.setOwner(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showMobilityScriptDialogs() {
|
||||||
|
for (Map.Entry<Integer, MobilityConfig> entry : mobilityScripts.entrySet()) {
|
||||||
|
Integer nodeId = entry.getKey();
|
||||||
|
CoreNode node = networkGraph.getVertex(nodeId);
|
||||||
|
MobilityConfig mobilityConfig = entry.getValue();
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
MobilityPlayerDialog mobilityPlayerDialog = new MobilityPlayerDialog(this, node);
|
||||||
|
mobilityPlayerDialog.setOwner(window);
|
||||||
|
mobilityPlayerDialogs.put(nodeId, mobilityPlayerDialog);
|
||||||
|
mobilityPlayerDialog.showDialog(mobilityConfig);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void onCoreMenuConnect(ActionEvent event) {
|
||||||
|
logger.info("showing connect!");
|
||||||
|
connectDialog.showDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void onOptionsMenuNodeTypes(ActionEvent event) {
|
||||||
|
nodeTypesDialog.showDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void onOptionsMenuBackground(ActionEvent event) {
|
||||||
|
backgroundDialog.showDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void onOptionsMenuLocation(ActionEvent event) {
|
||||||
|
locationDialog.showDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void onOptionsMenuPreferences(ActionEvent event) {
|
||||||
|
guiPreferencesDialog.showDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void onHelpMenuWebsite(ActionEvent event) {
|
||||||
|
application.getHostServices().showDocument("https://github.com/coreemu/core");
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void onHelpMenuDocumentation(ActionEvent event) {
|
||||||
|
application.getHostServices().showDocument("http://coreemu.github.io/core/");
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void onHelpMenuMailingList(ActionEvent event) {
|
||||||
|
application.getHostServices().showDocument("https://publists.nrl.navy.mil/mailman/listinfo/core-users");
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void onOpenXmlAction() {
|
||||||
|
FileChooser fileChooser = new FileChooser();
|
||||||
|
fileChooser.setTitle("Open Session");
|
||||||
|
fileChooser.setInitialDirectory(new File(configuration.getXmlPath()));
|
||||||
|
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("XML", "*.xml"));
|
||||||
|
try {
|
||||||
|
File file = fileChooser.showOpenDialog(window);
|
||||||
|
if (file != null) {
|
||||||
|
openXml(file);
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
Toast.error(String.format("Invalid XML directory: %s", configuration.getXmlPath()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openXml(File file) {
|
||||||
|
logger.info("opening session xml: {}", file.getPath());
|
||||||
|
try {
|
||||||
|
SessionOverview sessionOverview = coreClient.openSession(file);
|
||||||
|
Integer sessionId = sessionOverview.getId();
|
||||||
|
joinSession(sessionId);
|
||||||
|
Toast.info(String.format("Joined Session %s", sessionId));
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Toast.error("Error opening session xml", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void onSaveXmlAction() {
|
||||||
|
FileChooser fileChooser = new FileChooser();
|
||||||
|
fileChooser.setTitle("Save Session");
|
||||||
|
fileChooser.setInitialFileName("session.xml");
|
||||||
|
fileChooser.setInitialDirectory(new File(configuration.getXmlPath()));
|
||||||
|
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("XML", "*.xml"));
|
||||||
|
File file = fileChooser.showSaveDialog(window);
|
||||||
|
if (file != null) {
|
||||||
|
logger.info("saving session xml: {}", file.getPath());
|
||||||
|
try {
|
||||||
|
coreClient.saveSession(file);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Toast.error("Error saving session xml", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void onSessionMenu(ActionEvent event) {
|
||||||
|
logger.info("sessions menu clicked");
|
||||||
|
try {
|
||||||
|
sessionsDialog.showDialog();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Toast.error("Error retrieving sessions", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void onSessionNodesMenu(ActionEvent event) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void onSessionHooksMenu(ActionEvent event) {
|
||||||
|
hooksDialog.showDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void onSessionOptionsMenu(ActionEvent event) {
|
||||||
|
try {
|
||||||
|
List<ConfigGroup> configGroups = coreClient.getSessionConfig();
|
||||||
|
configDialog.showDialog("Session Options", configGroups, () -> {
|
||||||
|
List<ConfigOption> options = configDialog.getOptions();
|
||||||
|
try {
|
||||||
|
boolean result = coreClient.setSessionConfig(options);
|
||||||
|
if (result) {
|
||||||
|
Toast.info("Session options saved");
|
||||||
|
} else {
|
||||||
|
Toast.error("Failure to set session config");
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.error("error getting session config");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.error("error getting session config");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void onTestMenuCharts(ActionEvent event) {
|
||||||
|
chartDialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void onTestMenuGeo(ActionEvent event) {
|
||||||
|
geoDialog.showDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
coreWebSocket = new CoreWebSocket(this);
|
||||||
|
configuration = ConfigUtils.load();
|
||||||
|
String address = configuration.getCoreAddress();
|
||||||
|
int port = configuration.getCorePort();
|
||||||
|
logger.info("core connection: {}:{}", address, port);
|
||||||
|
connectDialog.setAddress(address);
|
||||||
|
connectDialog.setPort(port);
|
||||||
|
connectToCore(address, port);
|
||||||
|
|
||||||
|
logger.info("controller initialize");
|
||||||
|
swingNode.setContent(networkGraph.getGraphViewer());
|
||||||
|
|
||||||
|
// update graph preferences
|
||||||
|
networkGraph.updatePreferences(configuration);
|
||||||
|
|
||||||
|
// set node types / default services
|
||||||
|
graphToolbar.setupNodeTypes();
|
||||||
|
defaultServices = configuration.getNodeTypeConfigs().stream()
|
||||||
|
.collect(Collectors.toMap(NodeTypeConfig::getModel, NodeTypeConfig::getServices));
|
||||||
|
|
||||||
|
// set graph toolbar
|
||||||
|
borderPane.setLeft(graphToolbar);
|
||||||
|
|
||||||
|
// setup snackbar
|
||||||
|
Toast.setSnackbarRoot(stackPane);
|
||||||
|
|
||||||
|
// setup throughput menu item
|
||||||
|
throughputMenuItem.setOnAction(event -> executorService.submit(new ChangeThroughputTask()));
|
||||||
|
|
||||||
|
// node details
|
||||||
|
networkGraph.getGraphViewer().getPickedVertexState().addItemListener(event -> {
|
||||||
|
CoreNode node = (CoreNode) event.getItem();
|
||||||
|
logger.info("picked: {}", node.getName());
|
||||||
|
if (event.getStateChange() == ItemEvent.SELECTED) {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
nodeDetails.setNode(node);
|
||||||
|
borderPane.setRight(nodeDetails);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Platform.runLater(() -> borderPane.setRight(null));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// edge details
|
||||||
|
networkGraph.getGraphViewer().getPickedEdgeState().addItemListener(event -> {
|
||||||
|
CoreLink link = (CoreLink) event.getItem();
|
||||||
|
logger.info("picked: {} - {}", link.getNodeOne(), link.getNodeTwo());
|
||||||
|
if (event.getStateChange() == ItemEvent.SELECTED) {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
linkDetails.setLink(link);
|
||||||
|
borderPane.setRight(linkDetails);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Platform.runLater(() -> borderPane.setRight(null));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ChangeThroughputTask extends Task<Boolean> {
|
||||||
|
@Override
|
||||||
|
protected Boolean call() throws Exception {
|
||||||
|
if (throughputMenuItem.isSelected()) {
|
||||||
|
return coreClient.startThroughput();
|
||||||
|
} else {
|
||||||
|
return coreClient.stopThroughput();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void succeeded() {
|
||||||
|
if (getValue()) {
|
||||||
|
if (throughputMenuItem.isSelected()) {
|
||||||
|
networkGraph.setShowThroughput(true);
|
||||||
|
} else {
|
||||||
|
networkGraph.setShowThroughput(false);
|
||||||
|
networkGraph.getGraph().getEdges().forEach(edge -> edge.setThroughput(0));
|
||||||
|
networkGraph.getGraphViewer().repaint();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Toast.error("Failure changing throughput");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void failed() {
|
||||||
|
Toast.error("Error changing throughput", new RuntimeException(getException()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
72
corefx/src/main/java/com/core/Main.java
Normal file
72
corefx/src/main/java/com/core/Main.java
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
package com.core;
|
||||||
|
|
||||||
|
import com.core.utils.ConfigUtils;
|
||||||
|
import com.jfoenix.controls.JFXDecorator;
|
||||||
|
import com.jfoenix.svg.SVGGlyphLoader;
|
||||||
|
import javafx.application.Application;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.scene.Parent;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.text.Font;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
public class Main extends Application {
|
||||||
|
private static final Path LOG_FILE = Paths.get(System.getProperty("user.home"), ".core", "core.log");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(Stage window) throws Exception {
|
||||||
|
// set core dir property for logging
|
||||||
|
System.setProperty("core_log", LOG_FILE.toString());
|
||||||
|
|
||||||
|
// check for and create gui home directory
|
||||||
|
ConfigUtils.checkHomeDirectory();
|
||||||
|
|
||||||
|
// load svg icons
|
||||||
|
SVGGlyphLoader.loadGlyphsFont(getClass().getResourceAsStream("/icons/icomoon_material.svg"),
|
||||||
|
"icomoon.svg");
|
||||||
|
|
||||||
|
// load font
|
||||||
|
Font.loadFont(getClass().getResourceAsStream("/font/roboto/Roboto-Regular.ttf"), 10);
|
||||||
|
|
||||||
|
// load main fxml
|
||||||
|
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/main.fxml"));
|
||||||
|
Parent root = loader.load();
|
||||||
|
|
||||||
|
// window decorator
|
||||||
|
JFXDecorator decorator = new JFXDecorator(window, root);
|
||||||
|
decorator.setCustomMaximize(true);
|
||||||
|
decorator.setMaximized(true);
|
||||||
|
decorator.setTitle("CORE");
|
||||||
|
Image coreIcon = new Image(getClass().getResourceAsStream("/core-icon.png"));
|
||||||
|
decorator.setGraphic(new ImageView(coreIcon));
|
||||||
|
window.getIcons().add(coreIcon);
|
||||||
|
|
||||||
|
// create scene and set as current scene within window
|
||||||
|
Scene scene = new Scene(decorator);
|
||||||
|
scene.getStylesheets().add(getClass().getResource("/css/main.css").toExternalForm());
|
||||||
|
window.setScene(scene);
|
||||||
|
|
||||||
|
// update controller
|
||||||
|
Controller controller = loader.getController();
|
||||||
|
controller.setApplication(this);
|
||||||
|
controller.setWindow(window);
|
||||||
|
controller.setDecorator(decorator);
|
||||||
|
|
||||||
|
// configure window
|
||||||
|
window.setOnCloseRequest(event -> {
|
||||||
|
Platform.exit();
|
||||||
|
System.exit(0);
|
||||||
|
});
|
||||||
|
window.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
launch(args);
|
||||||
|
}
|
||||||
|
}
|
118
corefx/src/main/java/com/core/client/ICoreClient.java
Normal file
118
corefx/src/main/java/com/core/client/ICoreClient.java
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
package com.core.client;
|
||||||
|
|
||||||
|
import com.core.client.rest.ServiceFile;
|
||||||
|
import com.core.client.rest.WlanConfig;
|
||||||
|
import com.core.data.*;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public interface ICoreClient {
|
||||||
|
void setConnection(String address, int port);
|
||||||
|
|
||||||
|
boolean isLocalConnection();
|
||||||
|
|
||||||
|
Integer currentSession();
|
||||||
|
|
||||||
|
boolean startThroughput() throws IOException;
|
||||||
|
|
||||||
|
boolean stopThroughput() throws IOException;
|
||||||
|
|
||||||
|
void updateSession(Integer sessionId);
|
||||||
|
|
||||||
|
void updateState(SessionState state);
|
||||||
|
|
||||||
|
SessionOverview createSession() throws IOException;
|
||||||
|
|
||||||
|
boolean deleteSession(Integer sessionId) throws IOException;
|
||||||
|
|
||||||
|
List<SessionOverview> getSessions() throws IOException;
|
||||||
|
|
||||||
|
Session getSession(Integer sessionId) throws IOException;
|
||||||
|
|
||||||
|
boolean start(Collection<CoreNode> nodes, Collection<CoreLink> links, List<Hook> hooks) throws IOException;
|
||||||
|
|
||||||
|
boolean stop() throws IOException;
|
||||||
|
|
||||||
|
boolean setState(SessionState state) throws IOException;
|
||||||
|
|
||||||
|
Map<String, List<String>> getServices() throws IOException;
|
||||||
|
|
||||||
|
Map<String, List<String>> getDefaultServices() throws IOException;
|
||||||
|
|
||||||
|
boolean setDefaultServices(Map<String, Set<String>> defaults) throws IOException;
|
||||||
|
|
||||||
|
CoreService getService(CoreNode node, String serviceName) throws IOException;
|
||||||
|
|
||||||
|
boolean setService(CoreNode node, String serviceName, CoreService service) throws IOException;
|
||||||
|
|
||||||
|
String getServiceFile(CoreNode node, String serviceName, String fileName) throws IOException;
|
||||||
|
|
||||||
|
boolean startService(CoreNode node, String serviceName) throws IOException;
|
||||||
|
|
||||||
|
boolean stopService(CoreNode node, String serviceName) throws IOException;
|
||||||
|
|
||||||
|
boolean restartService(CoreNode node, String serviceName) throws IOException;
|
||||||
|
|
||||||
|
boolean validateService(CoreNode node, String serviceName) throws IOException;
|
||||||
|
|
||||||
|
boolean setServiceFile(CoreNode node, String serviceName, ServiceFile serviceFile) throws IOException;
|
||||||
|
|
||||||
|
List<ConfigGroup> getEmaneConfig(CoreNode node) throws IOException;
|
||||||
|
|
||||||
|
List<String> getEmaneModels() throws IOException;
|
||||||
|
|
||||||
|
boolean setEmaneConfig(CoreNode node, List<ConfigOption> options) throws IOException;
|
||||||
|
|
||||||
|
List<ConfigGroup> getEmaneModelConfig(Integer id, String model) throws IOException;
|
||||||
|
|
||||||
|
boolean setEmaneModelConfig(Integer id, String model, List<ConfigOption> options) throws IOException;
|
||||||
|
|
||||||
|
boolean isRunning();
|
||||||
|
|
||||||
|
void saveSession(File file) throws IOException;
|
||||||
|
|
||||||
|
SessionOverview openSession(File file) throws IOException;
|
||||||
|
|
||||||
|
List<ConfigGroup> getSessionConfig() throws IOException;
|
||||||
|
|
||||||
|
boolean setSessionConfig(List<ConfigOption> configOptions) throws IOException;
|
||||||
|
|
||||||
|
boolean createNode(CoreNode node) throws IOException;
|
||||||
|
|
||||||
|
String nodeCommand(CoreNode node, String command) throws IOException;
|
||||||
|
|
||||||
|
boolean editNode(CoreNode node) throws IOException;
|
||||||
|
|
||||||
|
boolean deleteNode(CoreNode node) throws IOException;
|
||||||
|
|
||||||
|
boolean createLink(CoreLink link) throws IOException;
|
||||||
|
|
||||||
|
boolean editLink(CoreLink link) throws IOException;
|
||||||
|
|
||||||
|
boolean createHook(Hook hook) throws IOException;
|
||||||
|
|
||||||
|
List<Hook> getHooks() throws IOException;
|
||||||
|
|
||||||
|
WlanConfig getWlanConfig(CoreNode node) throws IOException;
|
||||||
|
|
||||||
|
boolean setWlanConfig(CoreNode node, WlanConfig config) throws IOException;
|
||||||
|
|
||||||
|
String getTerminalCommand(CoreNode node) throws IOException;
|
||||||
|
|
||||||
|
Map<Integer, MobilityConfig> getMobilityConfigs() throws IOException;
|
||||||
|
|
||||||
|
boolean setMobilityConfig(CoreNode node, MobilityConfig config) throws IOException;
|
||||||
|
|
||||||
|
MobilityConfig getMobilityConfig(CoreNode node) throws IOException;
|
||||||
|
|
||||||
|
boolean mobilityAction(CoreNode node, String action) throws IOException;
|
||||||
|
|
||||||
|
LocationConfig getLocationConfig() throws IOException;
|
||||||
|
|
||||||
|
boolean setLocationConfig(LocationConfig config) throws IOException;
|
||||||
|
}
|
677
corefx/src/main/java/com/core/client/grpc/CoreGrpcClient.java
Normal file
677
corefx/src/main/java/com/core/client/grpc/CoreGrpcClient.java
Normal file
|
@ -0,0 +1,677 @@
|
||||||
|
package com.core.client.grpc;
|
||||||
|
|
||||||
|
import com.core.client.ICoreClient;
|
||||||
|
import com.core.client.rest.ServiceFile;
|
||||||
|
import com.core.client.rest.WlanConfig;
|
||||||
|
import com.core.data.*;
|
||||||
|
import com.google.protobuf.ByteString;
|
||||||
|
import io.grpc.ManagedChannel;
|
||||||
|
import io.grpc.ManagedChannelBuilder;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class CoreGrpcClient implements ICoreClient {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private String address;
|
||||||
|
private int port;
|
||||||
|
private Integer sessionId;
|
||||||
|
private SessionState sessionState;
|
||||||
|
private CoreApiGrpc.CoreApiBlockingStub blockingStub;
|
||||||
|
private ManagedChannel channel;
|
||||||
|
|
||||||
|
private CoreProto.Node nodeToProto(CoreNode node) {
|
||||||
|
CoreProto.Position position = CoreProto.Position.newBuilder()
|
||||||
|
.setX(node.getPosition().getX().floatValue())
|
||||||
|
.setY(node.getPosition().getY().floatValue())
|
||||||
|
.build();
|
||||||
|
CoreProto.Node.Builder builder = CoreProto.Node.newBuilder()
|
||||||
|
.addAllServices(node.getServices())
|
||||||
|
.setType(CoreProto.NodeType.forNumber(node.getType()))
|
||||||
|
.setPosition(position);
|
||||||
|
if (node.getId() != null) {
|
||||||
|
builder.setId(node.getId());
|
||||||
|
}
|
||||||
|
if (node.getName() != null) {
|
||||||
|
builder.setName(node.getName());
|
||||||
|
}
|
||||||
|
if (node.getEmane() != null) {
|
||||||
|
builder.setEmane(node.getEmane());
|
||||||
|
}
|
||||||
|
if (node.getModel() != null) {
|
||||||
|
builder.setModel(node.getModel());
|
||||||
|
}
|
||||||
|
if (node.getIcon() != null) {
|
||||||
|
builder.setIcon(node.getIcon());
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private CoreProto.LinkOptions linkOptionsToProto(CoreLinkOptions options) {
|
||||||
|
CoreProto.LinkOptions.Builder builder = CoreProto.LinkOptions.newBuilder();
|
||||||
|
boolean unidirectional = false;
|
||||||
|
if (options.getUnidirectional() != null && options.getUnidirectional() == 1) {
|
||||||
|
unidirectional = true;
|
||||||
|
}
|
||||||
|
if (options.getBandwidth() != null) {
|
||||||
|
builder.setBandwidth(options.getBandwidth().floatValue());
|
||||||
|
}
|
||||||
|
if (options.getBurst() != null) {
|
||||||
|
builder.setBurst(options.getBurst().floatValue());
|
||||||
|
}
|
||||||
|
if (options.getDelay() != null) {
|
||||||
|
builder.setDelay(options.getDelay().floatValue());
|
||||||
|
}
|
||||||
|
if (options.getDup() != null) {
|
||||||
|
builder.setDup(options.getDup().floatValue());
|
||||||
|
}
|
||||||
|
if (options.getJitter() != null) {
|
||||||
|
builder.setJitter(options.getJitter().floatValue());
|
||||||
|
}
|
||||||
|
if (options.getMburst() != null) {
|
||||||
|
builder.setMburst(options.getMburst().floatValue());
|
||||||
|
}
|
||||||
|
if (options.getMer() != null) {
|
||||||
|
builder.setMer(options.getMer().floatValue());
|
||||||
|
}
|
||||||
|
if (options.getPer() != null) {
|
||||||
|
builder.setPer(options.getPer().floatValue());
|
||||||
|
}
|
||||||
|
if (options.getKey() != null) {
|
||||||
|
builder.setKey(options.getKey().toString());
|
||||||
|
}
|
||||||
|
if (options.getOpaque() != null) {
|
||||||
|
builder.setOpaque(options.getOpaque());
|
||||||
|
}
|
||||||
|
builder.setUnidirectional(unidirectional);
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private CoreProto.Interface interfaceToProto(CoreInterface coreInterface) {
|
||||||
|
CoreProto.Interface.Builder builder = CoreProto.Interface.newBuilder();
|
||||||
|
if (coreInterface.getName() != null) {
|
||||||
|
builder.setName(coreInterface.getName());
|
||||||
|
}
|
||||||
|
if (coreInterface.getMac() != null) {
|
||||||
|
builder.setMac(coreInterface.getMac());
|
||||||
|
}
|
||||||
|
if (coreInterface.getIp4() != null) {
|
||||||
|
builder.setIp4(coreInterface.getIp4());
|
||||||
|
}
|
||||||
|
if (coreInterface.getIp4Mask() != null) {
|
||||||
|
builder.setIp4Mask(coreInterface.getIp4Mask());
|
||||||
|
}
|
||||||
|
if (coreInterface.getIp6() != null) {
|
||||||
|
builder.setIp6(coreInterface.getIp6());
|
||||||
|
}
|
||||||
|
if (coreInterface.getIp6Mask() != null) {
|
||||||
|
builder.setIp6Mask(Integer.parseInt(coreInterface.getIp6Mask()));
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setConnection(String address, int port) {
|
||||||
|
this.address = address;
|
||||||
|
this.port = port;
|
||||||
|
logger.info("set connection: {}:{}", this.address, this.port);
|
||||||
|
channel = ManagedChannelBuilder.forAddress(this.address, this.port).usePlaintext().build();
|
||||||
|
logger.info("channel: {}", channel);
|
||||||
|
blockingStub = CoreApiGrpc.newBlockingStub(channel);
|
||||||
|
logger.info("stub: {}", blockingStub);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLocalConnection() {
|
||||||
|
return address.equals("127.0.0.1") || address.equals("localhost");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer currentSession() {
|
||||||
|
return sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean startThroughput() throws IOException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean stopThroughput() throws IOException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateSession(Integer sessionId) {
|
||||||
|
this.sessionId = sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateState(SessionState state) {
|
||||||
|
sessionState = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SessionOverview createSession() throws IOException {
|
||||||
|
CoreProto.CreateSessionRequest request = CoreProto.CreateSessionRequest.newBuilder().build();
|
||||||
|
CoreProto.CreateSessionResponse response = blockingStub.createSession(request);
|
||||||
|
SessionOverview overview = new SessionOverview();
|
||||||
|
overview.setId(response.getId());
|
||||||
|
overview.setState(response.getStateValue());
|
||||||
|
overview.setNodes(0);
|
||||||
|
return overview;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean deleteSession(Integer sessionId) throws IOException {
|
||||||
|
CoreProto.DeleteSessionRequest request = CoreProto.DeleteSessionRequest.newBuilder().setId(sessionId).build();
|
||||||
|
return blockingStub.deleteSession(request).getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<SessionOverview> getSessions() throws IOException {
|
||||||
|
CoreProto.GetSessionsRequest request = CoreProto.GetSessionsRequest.newBuilder().build();
|
||||||
|
CoreProto.GetSessionsResponse response = blockingStub.getSessions(request);
|
||||||
|
List<SessionOverview> sessions = new ArrayList<>();
|
||||||
|
for (CoreProto.SessionSummary summary : response.getSessionsList()) {
|
||||||
|
SessionOverview overview = new SessionOverview();
|
||||||
|
overview.setId(summary.getId());
|
||||||
|
overview.setNodes(summary.getNodes());
|
||||||
|
overview.setState(summary.getStateValue());
|
||||||
|
sessions.add(overview);
|
||||||
|
}
|
||||||
|
return sessions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Session getSession(Integer sessionId) throws IOException {
|
||||||
|
CoreProto.GetSessionRequest request = CoreProto.GetSessionRequest.newBuilder().setId(sessionId).build();
|
||||||
|
CoreProto.GetSessionResponse response = blockingStub.getSession(request);
|
||||||
|
Session session = new Session();
|
||||||
|
for (CoreProto.Node protoNode : response.getSession().getNodesList()) {
|
||||||
|
CoreNode node = new CoreNode(protoNode.getId());
|
||||||
|
node.setName(protoNode.getName());
|
||||||
|
node.setEmane(protoNode.getEmane());
|
||||||
|
node.setIcon(protoNode.getIcon());
|
||||||
|
node.setModel(protoNode.getModel());
|
||||||
|
node.setServices(new HashSet<>(protoNode.getServicesList()));
|
||||||
|
node.getPosition().setX((double) protoNode.getPosition().getX());
|
||||||
|
node.getPosition().setY((double) protoNode.getPosition().getY());
|
||||||
|
node.getPosition().setZ((double) protoNode.getPosition().getZ());
|
||||||
|
node.setNodeType(NodeType.get(protoNode.getTypeValue()));
|
||||||
|
}
|
||||||
|
for (CoreProto.Link linkProto : response.getSession().getLinksList()) {
|
||||||
|
CoreLink link = new CoreLink();
|
||||||
|
link.setNodeOne(linkProto.getNodeOne());
|
||||||
|
link.setNodeTwo(linkProto.getNodeOne());
|
||||||
|
CoreProto.Interface interfaceOneProto = linkProto.getInterfaceOne();
|
||||||
|
CoreInterface interfaceOne = new CoreInterface();
|
||||||
|
interfaceOne.setId(interfaceOneProto.getId());
|
||||||
|
interfaceOne.setName(interfaceOneProto.getName());
|
||||||
|
interfaceOne.setMac(interfaceOneProto.getMac());
|
||||||
|
interfaceOne.setIp4(interfaceOneProto.getIp4());
|
||||||
|
interfaceOne.setIp4Mask(interfaceOneProto.getIp4Mask());
|
||||||
|
interfaceOne.setIp6(interfaceOneProto.getIp6());
|
||||||
|
interfaceOne.setIp6Mask(Integer.toString(interfaceOneProto.getIp6Mask()));
|
||||||
|
link.setInterfaceOne(interfaceOne);
|
||||||
|
|
||||||
|
CoreProto.Interface interfaceTwoProto = linkProto.getInterfaceTwo();
|
||||||
|
CoreInterface interfaceTwo = new CoreInterface();
|
||||||
|
interfaceTwo.setId(interfaceTwoProto.getId());
|
||||||
|
interfaceTwo.setName(interfaceTwoProto.getName());
|
||||||
|
interfaceTwo.setMac(interfaceTwoProto.getMac());
|
||||||
|
interfaceTwo.setIp4(interfaceTwoProto.getIp4());
|
||||||
|
interfaceTwo.setIp4Mask(interfaceTwoProto.getIp4Mask());
|
||||||
|
interfaceTwo.setIp6(interfaceTwoProto.getIp6());
|
||||||
|
interfaceTwo.setIp6Mask(Integer.toString(interfaceTwoProto.getIp6Mask()));
|
||||||
|
link.setInterfaceTwo(interfaceTwo);
|
||||||
|
|
||||||
|
CoreLinkOptions options = new CoreLinkOptions();
|
||||||
|
CoreProto.LinkOptions protoOptions = linkProto.getOptions();
|
||||||
|
options.setBandwidth((double) protoOptions.getBandwidth());
|
||||||
|
options.setDelay((double) protoOptions.getDelay());
|
||||||
|
options.setDup((double) protoOptions.getDup());
|
||||||
|
options.setJitter((double) protoOptions.getJitter());
|
||||||
|
options.setPer((double) protoOptions.getPer());
|
||||||
|
options.setBurst((double) protoOptions.getBurst());
|
||||||
|
options.setKey(Integer.parseInt(protoOptions.getKey()));
|
||||||
|
options.setMburst((double) protoOptions.getMburst());
|
||||||
|
options.setMer((double) protoOptions.getMer());
|
||||||
|
options.setOpaque(protoOptions.getOpaque());
|
||||||
|
options.setUnidirectional(protoOptions.getUnidirectional() ? 1 : 0);
|
||||||
|
link.setOptions(options);
|
||||||
|
}
|
||||||
|
session.setState(response.getSession().getStateValue());
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean start(Collection<CoreNode> nodes, Collection<CoreLink> links, List<Hook> hooks) throws IOException {
|
||||||
|
boolean result = setState(SessionState.DEFINITION);
|
||||||
|
if (!result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = setState(SessionState.CONFIGURATION);
|
||||||
|
if (!result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Hook hook : hooks) {
|
||||||
|
if (!createHook(hook)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (CoreNode node : nodes) {
|
||||||
|
// must pre-configure wlan nodes, if not already
|
||||||
|
if (node.getNodeType().getValue() == NodeType.WLAN) {
|
||||||
|
WlanConfig config = getWlanConfig(node);
|
||||||
|
setWlanConfig(node, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!createNode(node)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (CoreLink link : links) {
|
||||||
|
if (!createLink(link)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return setState(SessionState.INSTANTIATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean stop() throws IOException {
|
||||||
|
return setState(SessionState.SHUTDOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setState(SessionState state) throws IOException {
|
||||||
|
CoreProto.SetSessionStateRequest request = CoreProto.SetSessionStateRequest.newBuilder()
|
||||||
|
.setId(sessionId)
|
||||||
|
.setStateValue(state.getValue())
|
||||||
|
.build();
|
||||||
|
CoreProto.SetSessionStateResponse response = blockingStub.setSessionState(request);
|
||||||
|
return response.getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, List<String>> getServices() throws IOException {
|
||||||
|
CoreProto.GetServicesRequest request = CoreProto.GetServicesRequest.newBuilder().build();
|
||||||
|
CoreProto.GetServicesResponse response = blockingStub.getServices(request);
|
||||||
|
Map<String, List<String>> servicesMap = new HashMap<>();
|
||||||
|
for (CoreProto.Service protoService : response.getServicesList()) {
|
||||||
|
List<String> services = servicesMap.computeIfAbsent(protoService.getGroup(), x -> new ArrayList<>());
|
||||||
|
services.add(protoService.getName());
|
||||||
|
}
|
||||||
|
return servicesMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, List<String>> getDefaultServices() throws IOException {
|
||||||
|
CoreProto.GetServiceDefaultsRequest request = CoreProto.GetServiceDefaultsRequest.newBuilder().build();
|
||||||
|
CoreProto.GetServiceDefaultsResponse response = blockingStub.getServiceDefaults(request);
|
||||||
|
Map<String, List<String>> servicesMap = new HashMap<>();
|
||||||
|
for (CoreProto.ServiceDefaults serviceDefaults : response.getDefaultsList()) {
|
||||||
|
servicesMap.put(serviceDefaults.getNodeType(), serviceDefaults.getServicesList());
|
||||||
|
}
|
||||||
|
return servicesMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setDefaultServices(Map<String, Set<String>> defaults) throws IOException {
|
||||||
|
List<CoreProto.ServiceDefaults> allDefaults = new ArrayList<>();
|
||||||
|
for (Map.Entry<String, Set<String>> entry : defaults.entrySet()) {
|
||||||
|
String nodeType = entry.getKey();
|
||||||
|
Set<String> services = entry.getValue();
|
||||||
|
CoreProto.ServiceDefaults serviceDefaults = CoreProto.ServiceDefaults.newBuilder()
|
||||||
|
.setNodeType(nodeType)
|
||||||
|
.addAllServices(services)
|
||||||
|
.build();
|
||||||
|
allDefaults.add(serviceDefaults);
|
||||||
|
}
|
||||||
|
|
||||||
|
CoreProto.SetServiceDefaultsRequest request = CoreProto.SetServiceDefaultsRequest.newBuilder()
|
||||||
|
.setSession(sessionId)
|
||||||
|
.addAllDefaults(allDefaults)
|
||||||
|
.build();
|
||||||
|
CoreProto.SetServiceDefaultsResponse response = blockingStub.setServiceDefaults(request);
|
||||||
|
return response.getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CoreService getService(CoreNode node, String serviceName) throws IOException {
|
||||||
|
CoreProto.GetNodeServiceRequest request = CoreProto.GetNodeServiceRequest.newBuilder().build();
|
||||||
|
CoreProto.GetNodeServiceResponse response = blockingStub.getNodeService(request);
|
||||||
|
CoreProto.NodeServiceData nodeServiceData = response.getService();
|
||||||
|
CoreService service = new CoreService();
|
||||||
|
service.setShutdown(nodeServiceData.getShutdownList());
|
||||||
|
service.setStartup(nodeServiceData.getStartupList());
|
||||||
|
service.setValidate(nodeServiceData.getValidateList());
|
||||||
|
service.setConfigs(nodeServiceData.getConfigsList());
|
||||||
|
service.setDependencies(nodeServiceData.getDependenciesList());
|
||||||
|
service.setDirs(nodeServiceData.getDirsList());
|
||||||
|
service.setExecutables(nodeServiceData.getExecutablesList());
|
||||||
|
service.setMeta(nodeServiceData.getMeta());
|
||||||
|
service.setValidationMode(nodeServiceData.getValidationMode().name());
|
||||||
|
service.setValidationTimer(Integer.toString(nodeServiceData.getValidationTimer()));
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setService(CoreNode node, String serviceName, CoreService service) throws IOException {
|
||||||
|
CoreProto.SetNodeServiceRequest request = CoreProto.SetNodeServiceRequest.newBuilder()
|
||||||
|
.setId(node.getId())
|
||||||
|
.setSession(sessionId)
|
||||||
|
.setService(serviceName)
|
||||||
|
.build();
|
||||||
|
request.getShutdownList().addAll(service.getShutdown());
|
||||||
|
request.getValidateList().addAll(service.getValidate());
|
||||||
|
request.getStartupList().addAll(service.getStartup());
|
||||||
|
return blockingStub.setNodeService(request).getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getServiceFile(CoreNode node, String serviceName, String fileName) throws IOException {
|
||||||
|
CoreProto.GetNodeServiceFileRequest request = CoreProto.GetNodeServiceFileRequest.newBuilder()
|
||||||
|
.setSession(sessionId)
|
||||||
|
.setId(node.getId())
|
||||||
|
.setService(serviceName)
|
||||||
|
.build();
|
||||||
|
CoreProto.GetNodeServiceFileResponse response = blockingStub.getNodeServiceFile(request);
|
||||||
|
return response.getData().toStringUtf8();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean startService(CoreNode node, String serviceName) throws IOException {
|
||||||
|
CoreProto.ServiceActionRequest request = CoreProto.ServiceActionRequest.newBuilder()
|
||||||
|
.setSession(sessionId)
|
||||||
|
.setId(node.getId())
|
||||||
|
.setService(serviceName)
|
||||||
|
.setAction(CoreProto.ServiceAction.SERVICE_START)
|
||||||
|
.build();
|
||||||
|
return blockingStub.serviceAction(request).getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean stopService(CoreNode node, String serviceName) throws IOException {
|
||||||
|
CoreProto.ServiceActionRequest request = CoreProto.ServiceActionRequest.newBuilder()
|
||||||
|
.setSession(sessionId)
|
||||||
|
.setId(node.getId())
|
||||||
|
.setService(serviceName)
|
||||||
|
.setAction(CoreProto.ServiceAction.SERVICE_STOP)
|
||||||
|
.build();
|
||||||
|
return blockingStub.serviceAction(request).getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean restartService(CoreNode node, String serviceName) throws IOException {
|
||||||
|
CoreProto.ServiceActionRequest request = CoreProto.ServiceActionRequest.newBuilder()
|
||||||
|
.setSession(sessionId)
|
||||||
|
.setId(node.getId())
|
||||||
|
.setService(serviceName)
|
||||||
|
.setAction(CoreProto.ServiceAction.SERVICE_RESTART)
|
||||||
|
.build();
|
||||||
|
return blockingStub.serviceAction(request).getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean validateService(CoreNode node, String serviceName) throws IOException {
|
||||||
|
CoreProto.ServiceActionRequest request = CoreProto.ServiceActionRequest.newBuilder()
|
||||||
|
.setSession(sessionId)
|
||||||
|
.setId(node.getId())
|
||||||
|
.setService(serviceName)
|
||||||
|
.setAction(CoreProto.ServiceAction.SERVICE_VALIDATE)
|
||||||
|
.build();
|
||||||
|
return blockingStub.serviceAction(request).getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setServiceFile(CoreNode node, String serviceName, ServiceFile serviceFile) throws IOException {
|
||||||
|
CoreProto.SetNodeServiceFileRequest request = CoreProto.SetNodeServiceFileRequest.newBuilder()
|
||||||
|
.setSession(sessionId)
|
||||||
|
.setId(node.getId())
|
||||||
|
.setService(serviceName)
|
||||||
|
.setFile(serviceFile.getName())
|
||||||
|
.setData(ByteString.copyFromUtf8(serviceFile.getData()))
|
||||||
|
.build();
|
||||||
|
CoreProto.SetNodeServiceFileResponse response = blockingStub.setNodeServiceFile(request);
|
||||||
|
return response.getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ConfigGroup> getEmaneConfig(CoreNode node) throws IOException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getEmaneModels() throws IOException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setEmaneConfig(CoreNode node, List<ConfigOption> options) throws IOException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ConfigGroup> getEmaneModelConfig(Integer id, String model) throws IOException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setEmaneModelConfig(Integer id, String model, List<ConfigOption> options) throws IOException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRunning() {
|
||||||
|
return sessionState == SessionState.RUNTIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveSession(File file) throws IOException {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SessionOverview openSession(File file) throws IOException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ConfigGroup> getSessionConfig() throws IOException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setSessionConfig(List<ConfigOption> configOptions) throws IOException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean createNode(CoreNode node) throws IOException {
|
||||||
|
CoreProto.Node protoNode = nodeToProto(node);
|
||||||
|
CoreProto.AddNodeRequest request = CoreProto.AddNodeRequest.newBuilder()
|
||||||
|
.setSession(sessionId)
|
||||||
|
.setNode(protoNode)
|
||||||
|
.build();
|
||||||
|
blockingStub.addNode(request);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String nodeCommand(CoreNode node, String command) throws IOException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean editNode(CoreNode node) throws IOException {
|
||||||
|
CoreProto.Position position = CoreProto.Position.newBuilder()
|
||||||
|
.setX(node.getPosition().getX().floatValue())
|
||||||
|
.setY(node.getPosition().getY().floatValue())
|
||||||
|
.build();
|
||||||
|
CoreProto.EditNodeRequest request = CoreProto.EditNodeRequest.newBuilder()
|
||||||
|
.setSession(sessionId)
|
||||||
|
.setId(node.getId())
|
||||||
|
.setPosition(position)
|
||||||
|
.build();
|
||||||
|
CoreProto.EditNodeResponse response = blockingStub.editNode(request);
|
||||||
|
return response.getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean deleteNode(CoreNode node) throws IOException {
|
||||||
|
CoreProto.DeleteNodeRequest request = CoreProto.DeleteNodeRequest.newBuilder()
|
||||||
|
.build();
|
||||||
|
CoreProto.DeleteNodeResponse response = blockingStub.deleteNode(request);
|
||||||
|
return response.getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean createLink(CoreLink link) throws IOException {
|
||||||
|
CoreProto.Link.Builder builder = CoreProto.Link.newBuilder()
|
||||||
|
.setTypeValue(link.getType());
|
||||||
|
if (link.getNodeOne() != null) {
|
||||||
|
builder.setNodeOne(link.getNodeOne());
|
||||||
|
}
|
||||||
|
if (link.getNodeTwo() != null) {
|
||||||
|
builder.setNodeTwo(link.getNodeTwo());
|
||||||
|
}
|
||||||
|
if (link.getInterfaceOne() != null) {
|
||||||
|
builder.setInterfaceOne(interfaceToProto(link.getInterfaceOne()));
|
||||||
|
}
|
||||||
|
if (link.getInterfaceTwo() != null) {
|
||||||
|
builder.setInterfaceTwo(interfaceToProto(link.getInterfaceTwo()));
|
||||||
|
}
|
||||||
|
if (link.getOptions() != null) {
|
||||||
|
builder.setOptions(linkOptionsToProto(link.getOptions()));
|
||||||
|
}
|
||||||
|
CoreProto.Link protoLink = builder.build();
|
||||||
|
CoreProto.AddLinkRequest request = CoreProto.AddLinkRequest.newBuilder()
|
||||||
|
.setSession(sessionId)
|
||||||
|
.setLink(protoLink)
|
||||||
|
.build();
|
||||||
|
CoreProto.AddLinkResponse response = blockingStub.addLink(request);
|
||||||
|
return response.getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean editLink(CoreLink link) throws IOException {
|
||||||
|
CoreProto.EditLinkRequest.Builder builder = CoreProto.EditLinkRequest.newBuilder()
|
||||||
|
.setSession(sessionId);
|
||||||
|
if (link.getNodeOne() != null) {
|
||||||
|
builder.setNodeOne(link.getNodeOne());
|
||||||
|
}
|
||||||
|
if (link.getNodeTwo() != null) {
|
||||||
|
builder.setNodeTwo(link.getNodeTwo());
|
||||||
|
}
|
||||||
|
if (link.getInterfaceOne() != null) {
|
||||||
|
builder.setInterfaceOne(link.getInterfaceOne().getId());
|
||||||
|
}
|
||||||
|
if (link.getInterfaceTwo() != null) {
|
||||||
|
builder.setInterfaceTwo(link.getInterfaceTwo().getId());
|
||||||
|
}
|
||||||
|
if (link.getOptions() != null) {
|
||||||
|
CoreProto.LinkOptions protoOptions = linkOptionsToProto(link.getOptions());
|
||||||
|
builder.setOptions(protoOptions);
|
||||||
|
}
|
||||||
|
CoreProto.EditLinkRequest request = builder.build();
|
||||||
|
CoreProto.EditLinkResponse response = blockingStub.editLink(request);
|
||||||
|
return response.getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean createHook(Hook hook) throws IOException {
|
||||||
|
CoreProto.Hook hookProto = CoreProto.Hook.newBuilder()
|
||||||
|
.setStateValue(hook.getState())
|
||||||
|
.setData(ByteString.copyFromUtf8(hook.getData()))
|
||||||
|
.setFile(hook.getFile())
|
||||||
|
.build();
|
||||||
|
CoreProto.AddHookRequest request = CoreProto.AddHookRequest.newBuilder()
|
||||||
|
.setHook(hookProto)
|
||||||
|
.build();
|
||||||
|
CoreProto.AddHookResponse response = blockingStub.addHook(request);
|
||||||
|
return response.getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Hook> getHooks() throws IOException {
|
||||||
|
CoreProto.GetHooksRequest request = CoreProto.GetHooksRequest.newBuilder().setSession(sessionId).build();
|
||||||
|
CoreProto.GetHooksResponse response = blockingStub.getHooks(request);
|
||||||
|
List<Hook> hooks = new ArrayList<>();
|
||||||
|
for (CoreProto.Hook protoHook : response.getHooksList()) {
|
||||||
|
Hook hook = new Hook();
|
||||||
|
hook.setFile(protoHook.getFile());
|
||||||
|
hook.setData(protoHook.getData().toStringUtf8());
|
||||||
|
hook.setState(protoHook.getStateValue());
|
||||||
|
hooks.add(hook);
|
||||||
|
}
|
||||||
|
return hooks;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WlanConfig getWlanConfig(CoreNode node) throws IOException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setWlanConfig(CoreNode node, WlanConfig config) throws IOException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTerminalCommand(CoreNode node) throws IOException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<Integer, MobilityConfig> getMobilityConfigs() throws IOException {
|
||||||
|
CoreProto.GetMobilityConfigsRequest request = CoreProto.GetMobilityConfigsRequest.newBuilder()
|
||||||
|
.setSession(sessionId).build();
|
||||||
|
CoreProto.GetMobilityConfigsResponse response = blockingStub.getMobilityConfigs(request);
|
||||||
|
|
||||||
|
Map<Integer, MobilityConfig> mobilityConfigs = new HashMap<>();
|
||||||
|
for (Integer nodeId : response.getConfigsMap().keySet()) {
|
||||||
|
CoreProto.GetMobilityConfigsResponse.MobilityConfig protoMobilityConfig = response.getConfigsMap()
|
||||||
|
.get(nodeId);
|
||||||
|
MobilityConfig mobilityConfig = new MobilityConfig();
|
||||||
|
CoreProto.ConfigGroup configGroup = protoMobilityConfig.getGroups(0);
|
||||||
|
mobilityConfigs.put(nodeId, mobilityConfig);
|
||||||
|
}
|
||||||
|
return mobilityConfigs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setMobilityConfig(CoreNode node, MobilityConfig config) throws IOException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MobilityConfig getMobilityConfig(CoreNode node) throws IOException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean mobilityAction(CoreNode node, String action) throws IOException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LocationConfig getLocationConfig() throws IOException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setLocationConfig(LocationConfig config) throws IOException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
415
corefx/src/main/java/com/core/client/rest/CoreRestClient.java
Normal file
415
corefx/src/main/java/com/core/client/rest/CoreRestClient.java
Normal file
|
@ -0,0 +1,415 @@
|
||||||
|
package com.core.client.rest;
|
||||||
|
|
||||||
|
import com.core.client.ICoreClient;
|
||||||
|
import com.core.data.*;
|
||||||
|
import com.core.utils.WebUtils;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class CoreRestClient implements ICoreClient {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private String address;
|
||||||
|
private int port;
|
||||||
|
private Integer sessionId;
|
||||||
|
private SessionState sessionState;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setConnection(String address, int port) {
|
||||||
|
this.address = address;
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLocalConnection() {
|
||||||
|
return address.equals("127.0.0.1") || address.equals("localhost");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer currentSession() {
|
||||||
|
return sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateState(SessionState state) {
|
||||||
|
sessionState = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateSession(Integer sessionId) {
|
||||||
|
this.sessionId = sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getUrl(String path) {
|
||||||
|
return String.format("http://%s:%s/%s", address, port, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SessionOverview createSession() throws IOException {
|
||||||
|
String url = getUrl("sessions");
|
||||||
|
return WebUtils.post(url, SessionOverview.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean deleteSession(Integer sessionId) throws IOException {
|
||||||
|
String path = String.format("sessions/%s", sessionId);
|
||||||
|
String url = getUrl(path);
|
||||||
|
return WebUtils.delete(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, List<String>> getServices() throws IOException {
|
||||||
|
String url = getUrl("services");
|
||||||
|
GetServices getServices = WebUtils.getJson(url, GetServices.class);
|
||||||
|
return getServices.getGroups();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Session getSession(Integer sessionId) throws IOException {
|
||||||
|
String path = String.format("sessions/%s", sessionId);
|
||||||
|
String url = getUrl(path);
|
||||||
|
return WebUtils.getJson(url, Session.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<SessionOverview> getSessions() throws IOException {
|
||||||
|
String url = getUrl("sessions");
|
||||||
|
GetSessions getSessions = WebUtils.getJson(url, GetSessions.class);
|
||||||
|
return getSessions.getSessions();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean start(Collection<CoreNode> nodes, Collection<CoreLink> links, List<Hook> hooks) throws IOException {
|
||||||
|
boolean result = setState(SessionState.DEFINITION);
|
||||||
|
if (!result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = setState(SessionState.CONFIGURATION);
|
||||||
|
if (!result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Hook hook : hooks) {
|
||||||
|
if (!createHook(hook)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (CoreNode node : nodes) {
|
||||||
|
// must pre-configure wlan nodes, if not already
|
||||||
|
if (node.getNodeType().getValue() == NodeType.WLAN) {
|
||||||
|
WlanConfig config = getWlanConfig(node);
|
||||||
|
setWlanConfig(node, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!createNode(node)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (CoreLink link : links) {
|
||||||
|
if (!createLink(link)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return setState(SessionState.INSTANTIATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean stop() throws IOException {
|
||||||
|
return setState(SessionState.SHUTDOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setState(SessionState state) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/state", sessionId));
|
||||||
|
Map<String, Integer> data = new HashMap<>();
|
||||||
|
data.put("state", state.getValue());
|
||||||
|
boolean result = WebUtils.putJson(url, data);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
sessionState = state;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean uploadFile(File file) throws IOException {
|
||||||
|
String url = getUrl("upload");
|
||||||
|
return WebUtils.postFile(url, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean startThroughput() throws IOException {
|
||||||
|
String url = getUrl("throughput/start");
|
||||||
|
return WebUtils.putJson(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean stopThroughput() throws IOException {
|
||||||
|
String url = getUrl("throughput/stop");
|
||||||
|
return WebUtils.putJson(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, List<String>> getDefaultServices() throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/services/default", sessionId));
|
||||||
|
GetDefaultServices getDefaultServices = WebUtils.getJson(url, GetDefaultServices.class);
|
||||||
|
return getDefaultServices.getDefaults();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setDefaultServices(Map<String, Set<String>> defaults) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/services/default", sessionId));
|
||||||
|
return WebUtils.postJson(url, defaults);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CoreService getService(CoreNode node, String serviceName) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s", sessionId, node.getId(), serviceName));
|
||||||
|
return WebUtils.getJson(url, CoreService.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setService(CoreNode node, String serviceName, CoreService service) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s", sessionId, node.getId(), serviceName));
|
||||||
|
return WebUtils.putJson(url, service);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getServiceFile(CoreNode node, String serviceName, String fileName) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s/file", sessionId, node.getId(),
|
||||||
|
serviceName));
|
||||||
|
Map<String, String> args = new HashMap<>();
|
||||||
|
args.put("file", fileName);
|
||||||
|
return WebUtils.getJson(url, String.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean startService(CoreNode node, String serviceName) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s/start", sessionId, node.getId(),
|
||||||
|
serviceName));
|
||||||
|
return WebUtils.putJson(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean stopService(CoreNode node, String serviceName) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s/stop", sessionId, node.getId(),
|
||||||
|
serviceName));
|
||||||
|
return WebUtils.putJson(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean restartService(CoreNode node, String serviceName) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s/restart", sessionId, node.getId(),
|
||||||
|
serviceName));
|
||||||
|
return WebUtils.putJson(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean validateService(CoreNode node, String serviceName) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s/validate", sessionId, node.getId(),
|
||||||
|
serviceName));
|
||||||
|
return WebUtils.putJson(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setServiceFile(CoreNode node, String serviceName, ServiceFile serviceFile) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s/file", sessionId, node.getId(),
|
||||||
|
serviceName));
|
||||||
|
return WebUtils.putJson(url, serviceFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getEmaneModels() throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/emane/models", sessionId));
|
||||||
|
GetEmaneModels getEmaneModels = WebUtils.getJson(url, GetEmaneModels.class);
|
||||||
|
return getEmaneModels.getModels();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ConfigGroup> getEmaneModelConfig(Integer id, String model) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/emane/model/config", sessionId));
|
||||||
|
Map<String, String> args = new HashMap<>();
|
||||||
|
args.put("node", id.toString());
|
||||||
|
args.put("name", model);
|
||||||
|
GetConfig getConfig = WebUtils.getJson(url, GetConfig.class, args);
|
||||||
|
return getConfig.getGroups();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ConfigGroup> getEmaneConfig(CoreNode node) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/emane/config", sessionId));
|
||||||
|
Map<String, String> args = new HashMap<>();
|
||||||
|
args.put("node", node.getId().toString());
|
||||||
|
GetConfig getConfig = WebUtils.getJson(url, GetConfig.class, args);
|
||||||
|
return getConfig.getGroups();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setEmaneConfig(CoreNode node, List<ConfigOption> options) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/emane/config", sessionId));
|
||||||
|
SetEmaneConfig setEmaneConfig = new SetEmaneConfig();
|
||||||
|
setEmaneConfig.setNode(node.getId());
|
||||||
|
setEmaneConfig.setValues(options);
|
||||||
|
return WebUtils.putJson(url, setEmaneConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setEmaneModelConfig(Integer id, String model, List<ConfigOption> options) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/emane/model/config", sessionId));
|
||||||
|
SetEmaneModelConfig setEmaneModelConfig = new SetEmaneModelConfig();
|
||||||
|
setEmaneModelConfig.setNode(id);
|
||||||
|
setEmaneModelConfig.setName(model);
|
||||||
|
setEmaneModelConfig.setValues(options);
|
||||||
|
return WebUtils.putJson(url, setEmaneModelConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRunning() {
|
||||||
|
return sessionState == SessionState.RUNTIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveSession(File file) throws IOException {
|
||||||
|
String path = String.format("sessions/%s/xml", sessionId);
|
||||||
|
String url = getUrl(path);
|
||||||
|
WebUtils.getFile(url, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SessionOverview openSession(File file) throws IOException {
|
||||||
|
String url = getUrl("sessions/xml");
|
||||||
|
return WebUtils.postFile(url, file, SessionOverview.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ConfigGroup> getSessionConfig() throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/options", sessionId));
|
||||||
|
GetConfig getConfig = WebUtils.getJson(url, GetConfig.class);
|
||||||
|
return getConfig.getGroups();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setSessionConfig(List<ConfigOption> configOptions) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/options", sessionId));
|
||||||
|
SetConfig setConfig = new SetConfig(configOptions);
|
||||||
|
return WebUtils.putJson(url, setConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LocationConfig getLocationConfig() throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/location", sessionId));
|
||||||
|
return WebUtils.getJson(url, LocationConfig.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setLocationConfig(LocationConfig config) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/location", sessionId));
|
||||||
|
return WebUtils.putJson(url, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String nodeCommand(CoreNode node, String command) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/nodes/%s/command", sessionId, node.getId()));
|
||||||
|
return WebUtils.putJson(url, command, String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean createNode(CoreNode node) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/nodes", sessionId));
|
||||||
|
return WebUtils.postJson(url, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean editNode(CoreNode node) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/nodes/%s", sessionId, node.getId()));
|
||||||
|
return WebUtils.putJson(url, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean deleteNode(CoreNode node) throws IOException {
|
||||||
|
String url = getUrl(String.format("/sessions/%s/nodes/%s", sessionId, node.getId()));
|
||||||
|
return WebUtils.delete(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean createLink(CoreLink link) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/links", sessionId));
|
||||||
|
return WebUtils.postJson(url, link);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean editLink(CoreLink link) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/links", sessionId));
|
||||||
|
return WebUtils.putJson(url, link);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean createHook(Hook hook) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/hooks", sessionId));
|
||||||
|
return WebUtils.postJson(url, hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Hook> getHooks() throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/hooks", sessionId));
|
||||||
|
GetHooks getHooks = WebUtils.getJson(url, GetHooks.class);
|
||||||
|
return getHooks.getHooks();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WlanConfig getWlanConfig(CoreNode node) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/nodes/%s/wlan", sessionId, node.getId()));
|
||||||
|
return WebUtils.getJson(url, WlanConfig.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setWlanConfig(CoreNode node, WlanConfig config) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/nodes/%s/wlan", sessionId, node.getId()));
|
||||||
|
return WebUtils.putJson(url, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTerminalCommand(CoreNode node) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/nodes/%s/terminal", sessionId, node.getId()));
|
||||||
|
return WebUtils.getJson(url, String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setMobilityConfig(CoreNode node, MobilityConfig config) throws IOException {
|
||||||
|
boolean uploaded = uploadFile(config.getScriptFile());
|
||||||
|
if (!uploaded) {
|
||||||
|
throw new IOException("failed to upload mobility script");
|
||||||
|
}
|
||||||
|
|
||||||
|
String url = getUrl(String.format("sessions/%s/nodes/%s/mobility", sessionId, node.getId()));
|
||||||
|
config.setFile(config.getScriptFile().getName());
|
||||||
|
return WebUtils.postJson(url, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<Integer, MobilityConfig> getMobilityConfigs() throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/mobility/configs", sessionId));
|
||||||
|
GetMobilityConfigs getMobilityConfigs = WebUtils.getJson(url, GetMobilityConfigs.class);
|
||||||
|
return getMobilityConfigs.getConfigurations();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MobilityConfig getMobilityConfig(CoreNode node) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/nodes/%s/mobility", sessionId, node.getId()));
|
||||||
|
return WebUtils.getJson(url, MobilityConfig.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean mobilityAction(CoreNode node, String action) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/nodes/%s/mobility/%s", sessionId, node.getId(), action));
|
||||||
|
return WebUtils.putJson(url);
|
||||||
|
}
|
||||||
|
}
|
12
corefx/src/main/java/com/core/client/rest/GetConfig.java
Normal file
12
corefx/src/main/java/com/core/client/rest/GetConfig.java
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package com.core.client.rest;
|
||||||
|
|
||||||
|
import com.core.data.ConfigGroup;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class GetConfig {
|
||||||
|
private List<ConfigGroup> groups = new ArrayList<>();
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.core.client.rest;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class GetDefaultServices {
|
||||||
|
private Map<String, List<String>> defaults = new HashMap<>();
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.core.client.rest;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class GetEmaneModels {
|
||||||
|
private List<String> models = new ArrayList<>();
|
||||||
|
}
|
12
corefx/src/main/java/com/core/client/rest/GetHooks.java
Normal file
12
corefx/src/main/java/com/core/client/rest/GetHooks.java
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package com.core.client.rest;
|
||||||
|
|
||||||
|
import com.core.data.Hook;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class GetHooks {
|
||||||
|
private List<Hook> hooks = new ArrayList<>();
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.core.client.rest;
|
||||||
|
|
||||||
|
import com.core.data.MobilityConfig;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class GetMobilityConfigs {
|
||||||
|
private Map<Integer, MobilityConfig> configurations = new HashMap<>();
|
||||||
|
}
|
13
corefx/src/main/java/com/core/client/rest/GetServices.java
Normal file
13
corefx/src/main/java/com/core/client/rest/GetServices.java
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package com.core.client.rest;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class GetServices {
|
||||||
|
private Map<String, List<String>> groups;
|
||||||
|
}
|
14
corefx/src/main/java/com/core/client/rest/GetSessions.java
Normal file
14
corefx/src/main/java/com/core/client/rest/GetSessions.java
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package com.core.client.rest;
|
||||||
|
|
||||||
|
import com.core.data.SessionOverview;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class GetSessions {
|
||||||
|
private List<SessionOverview> sessions = new ArrayList<>();
|
||||||
|
}
|
13
corefx/src/main/java/com/core/client/rest/ServiceFile.java
Normal file
13
corefx/src/main/java/com/core/client/rest/ServiceFile.java
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package com.core.client.rest;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class ServiceFile {
|
||||||
|
private String name;
|
||||||
|
private String data;
|
||||||
|
}
|
16
corefx/src/main/java/com/core/client/rest/SetConfig.java
Normal file
16
corefx/src/main/java/com/core/client/rest/SetConfig.java
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package com.core.client.rest;
|
||||||
|
|
||||||
|
import com.core.data.ConfigOption;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class SetConfig {
|
||||||
|
private List<ConfigOption> values = new ArrayList<>();
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.core.client.rest;
|
||||||
|
|
||||||
|
import com.core.data.ConfigOption;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class SetEmaneConfig {
|
||||||
|
private Integer node;
|
||||||
|
private List<ConfigOption> values = new ArrayList<>();
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.core.client.rest;
|
||||||
|
|
||||||
|
import com.core.data.ConfigOption;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class SetEmaneModelConfig {
|
||||||
|
private Integer node;
|
||||||
|
private String name;
|
||||||
|
private List<ConfigOption> values = new ArrayList<>();
|
||||||
|
}
|
12
corefx/src/main/java/com/core/client/rest/WlanConfig.java
Normal file
12
corefx/src/main/java/com/core/client/rest/WlanConfig.java
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package com.core.client.rest;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class WlanConfig {
|
||||||
|
private String range;
|
||||||
|
private String bandwidth;
|
||||||
|
private String jitter;
|
||||||
|
private String delay;
|
||||||
|
private String error;
|
||||||
|
}
|
9
corefx/src/main/java/com/core/data/BridgeThroughput.java
Normal file
9
corefx/src/main/java/com/core/data/BridgeThroughput.java
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package com.core.data;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class BridgeThroughput {
|
||||||
|
private int node;
|
||||||
|
private Double throughput;
|
||||||
|
}
|
40
corefx/src/main/java/com/core/data/ConfigDataType.java
Normal file
40
corefx/src/main/java/com/core/data/ConfigDataType.java
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package com.core.data;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public enum ConfigDataType {
|
||||||
|
UINT8(1),
|
||||||
|
UINT16(2),
|
||||||
|
UINT32(3),
|
||||||
|
UINT64(4),
|
||||||
|
INT8(5),
|
||||||
|
INT16(6),
|
||||||
|
INT32(7),
|
||||||
|
INT64(8),
|
||||||
|
FLOAT(9),
|
||||||
|
STRING(10),
|
||||||
|
BOOL(11);
|
||||||
|
|
||||||
|
private static final Map<Integer, ConfigDataType> LOOKUP = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
Arrays.stream(ConfigDataType.values()).forEach(x -> LOOKUP.put(x.getValue(), x));
|
||||||
|
}
|
||||||
|
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
ConfigDataType(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static ConfigDataType get(int value) {
|
||||||
|
return LOOKUP.get(value);
|
||||||
|
}
|
||||||
|
}
|
12
corefx/src/main/java/com/core/data/ConfigGroup.java
Normal file
12
corefx/src/main/java/com/core/data/ConfigGroup.java
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package com.core.data;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ConfigGroup {
|
||||||
|
private String name;
|
||||||
|
private List<ConfigOption> options = new ArrayList<>();
|
||||||
|
}
|
15
corefx/src/main/java/com/core/data/ConfigOption.java
Normal file
15
corefx/src/main/java/com/core/data/ConfigOption.java
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package com.core.data;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ConfigOption {
|
||||||
|
private String label;
|
||||||
|
private String name;
|
||||||
|
private String value;
|
||||||
|
private Integer type;
|
||||||
|
private List<String> select = new ArrayList<>();
|
||||||
|
}
|
19
corefx/src/main/java/com/core/data/CoreEvent.java
Normal file
19
corefx/src/main/java/com/core/data/CoreEvent.java
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package com.core.data;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonSetter;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class CoreEvent {
|
||||||
|
private Integer session;
|
||||||
|
private Integer node;
|
||||||
|
private String name;
|
||||||
|
private Double time;
|
||||||
|
private EventType eventType;
|
||||||
|
private String data;
|
||||||
|
|
||||||
|
@JsonSetter("event_type")
|
||||||
|
public void setEventType(int value) {
|
||||||
|
eventType = EventType.get(value);
|
||||||
|
}
|
||||||
|
}
|
19
corefx/src/main/java/com/core/data/CoreInterface.java
Normal file
19
corefx/src/main/java/com/core/data/CoreInterface.java
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package com.core.data;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class CoreInterface {
|
||||||
|
private Integer id;
|
||||||
|
private String name;
|
||||||
|
private String mac;
|
||||||
|
private String ip4;
|
||||||
|
@JsonProperty("ip4mask")
|
||||||
|
private Integer ip4Mask;
|
||||||
|
private String ip6;
|
||||||
|
@JsonProperty("ip6mask")
|
||||||
|
private String ip6Mask;
|
||||||
|
}
|
56
corefx/src/main/java/com/core/data/CoreLink.java
Normal file
56
corefx/src/main/java/com/core/data/CoreLink.java
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package com.core.data;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
|
||||||
|
public class CoreLink {
|
||||||
|
@EqualsAndHashCode.Include
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private Float weight = 1.0f;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private boolean loaded = true;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private double throughput;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private boolean visible = true;
|
||||||
|
|
||||||
|
@JsonProperty("message_type")
|
||||||
|
private Integer messageType;
|
||||||
|
|
||||||
|
private Integer type = 1;
|
||||||
|
|
||||||
|
@JsonProperty("node_one")
|
||||||
|
private Integer nodeOne;
|
||||||
|
|
||||||
|
@JsonProperty("node_two")
|
||||||
|
private Integer nodeTwo;
|
||||||
|
|
||||||
|
@JsonProperty("interface_one")
|
||||||
|
private CoreInterface interfaceOne;
|
||||||
|
|
||||||
|
@JsonProperty("interface_two")
|
||||||
|
private CoreInterface interfaceTwo;
|
||||||
|
|
||||||
|
private CoreLinkOptions options = new CoreLinkOptions();
|
||||||
|
|
||||||
|
public CoreLink(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
this.weight = (float) id;
|
||||||
|
this.loaded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isWireless() {
|
||||||
|
return interfaceOne == null && interfaceTwo == null;
|
||||||
|
}
|
||||||
|
}
|
21
corefx/src/main/java/com/core/data/CoreLinkOptions.java
Normal file
21
corefx/src/main/java/com/core/data/CoreLinkOptions.java
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package com.core.data;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class CoreLinkOptions {
|
||||||
|
private String opaque;
|
||||||
|
private Integer session;
|
||||||
|
private Double jitter;
|
||||||
|
private Integer key;
|
||||||
|
private Double mburst;
|
||||||
|
private Double mer;
|
||||||
|
private Double per;
|
||||||
|
private Double bandwidth;
|
||||||
|
private Double burst;
|
||||||
|
private Double delay;
|
||||||
|
private Double dup;
|
||||||
|
private Integer unidirectional;
|
||||||
|
}
|
59
corefx/src/main/java/com/core/data/CoreNode.java
Normal file
59
corefx/src/main/java/com/core/data/CoreNode.java
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
package com.core.data;
|
||||||
|
|
||||||
|
import com.core.graph.RadioIcon;
|
||||||
|
import com.core.utils.IconUtils;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import edu.uci.ics.jung.visualization.LayeredIcon;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
|
||||||
|
public class CoreNode {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
@EqualsAndHashCode.Include
|
||||||
|
private Integer id;
|
||||||
|
private String name;
|
||||||
|
private Integer type;
|
||||||
|
private String model;
|
||||||
|
private Position position = new Position();
|
||||||
|
private Set<String> services = new HashSet<>();
|
||||||
|
private String emane;
|
||||||
|
private String url;
|
||||||
|
@JsonIgnore
|
||||||
|
private NodeType nodeType;
|
||||||
|
@JsonIgnore
|
||||||
|
private String icon;
|
||||||
|
@JsonIgnore
|
||||||
|
private boolean loaded = true;
|
||||||
|
@JsonIgnore
|
||||||
|
private LayeredIcon graphIcon;
|
||||||
|
@JsonIgnore
|
||||||
|
private RadioIcon radioIcon = new RadioIcon();
|
||||||
|
|
||||||
|
public CoreNode(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = String.format("Node%s", this.id);
|
||||||
|
this.loaded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNodeType(NodeType nodeType) {
|
||||||
|
type = nodeType.getValue();
|
||||||
|
model = nodeType.getModel();
|
||||||
|
icon = nodeType.getIcon();
|
||||||
|
if (icon.startsWith("file:")) {
|
||||||
|
graphIcon = IconUtils.getExternalLayeredIcon(icon);
|
||||||
|
} else {
|
||||||
|
graphIcon = IconUtils.getLayeredIcon(icon);
|
||||||
|
}
|
||||||
|
graphIcon.add(radioIcon);
|
||||||
|
this.nodeType = nodeType;
|
||||||
|
}
|
||||||
|
}
|
23
corefx/src/main/java/com/core/data/CoreService.java
Normal file
23
corefx/src/main/java/com/core/data/CoreService.java
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package com.core.data;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class CoreService {
|
||||||
|
private List<String> executables = new ArrayList<>();
|
||||||
|
private List<String> dependencies = new ArrayList<>();
|
||||||
|
private List<String> dirs = new ArrayList<>();
|
||||||
|
private List<String> configs = new ArrayList<>();
|
||||||
|
private List<String> startup = new ArrayList<>();
|
||||||
|
private List<String> validate = new ArrayList<>();
|
||||||
|
@JsonProperty("validation_mode")
|
||||||
|
private String validationMode;
|
||||||
|
@JsonProperty("validation_timer")
|
||||||
|
private String validationTimer;
|
||||||
|
private List<String> shutdown = new ArrayList<>();
|
||||||
|
private String meta;
|
||||||
|
}
|
45
corefx/src/main/java/com/core/data/EventType.java
Normal file
45
corefx/src/main/java/com/core/data/EventType.java
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package com.core.data;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public enum EventType {
|
||||||
|
NONE(0),
|
||||||
|
DEFINITION_STATE(1),
|
||||||
|
CONFIGURATION_STATE(2),
|
||||||
|
INSTANTIATION_STATE(3),
|
||||||
|
RUNTIME_STATE(4),
|
||||||
|
DATACOLLECT_STATE(5),
|
||||||
|
SHUTDOWN_STATE(6),
|
||||||
|
START(7),
|
||||||
|
STOP(8),
|
||||||
|
PAUSE(9),
|
||||||
|
RESTART(10),
|
||||||
|
FILE_OPEN(11),
|
||||||
|
FILE_SAVE(12),
|
||||||
|
SCHEDULED(13),
|
||||||
|
RECONFIGURE(14),
|
||||||
|
INSTANTIATION_COMPLETE(15);
|
||||||
|
|
||||||
|
private static final Map<Integer, EventType> LOOKUP = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
Arrays.stream(EventType.values()).forEach(x -> LOOKUP.put(x.getValue(), x));
|
||||||
|
}
|
||||||
|
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
EventType(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static EventType get(int value) {
|
||||||
|
return LOOKUP.get(value);
|
||||||
|
}
|
||||||
|
}
|
13
corefx/src/main/java/com/core/data/Hook.java
Normal file
13
corefx/src/main/java/com/core/data/Hook.java
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package com.core.data;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class Hook {
|
||||||
|
private String file;
|
||||||
|
private Integer state;
|
||||||
|
@JsonIgnore
|
||||||
|
private String stateDisplay;
|
||||||
|
private String data;
|
||||||
|
}
|
12
corefx/src/main/java/com/core/data/InterfaceThroughput.java
Normal file
12
corefx/src/main/java/com/core/data/InterfaceThroughput.java
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package com.core.data;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class InterfaceThroughput {
|
||||||
|
private int node;
|
||||||
|
@JsonProperty("interface")
|
||||||
|
private int nodeInterface;
|
||||||
|
private double throughput;
|
||||||
|
}
|
31
corefx/src/main/java/com/core/data/LinkTypes.java
Normal file
31
corefx/src/main/java/com/core/data/LinkTypes.java
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package com.core.data;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public enum LinkTypes {
|
||||||
|
WIRELESS(0),
|
||||||
|
WIRED(1);
|
||||||
|
|
||||||
|
private static final Map<Integer, LinkTypes> LOOKUP = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (LinkTypes state : LinkTypes.values()) {
|
||||||
|
LOOKUP.put(state.getValue(), state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
LinkTypes(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LinkTypes get(int value) {
|
||||||
|
return LOOKUP.get(value);
|
||||||
|
}
|
||||||
|
}
|
10
corefx/src/main/java/com/core/data/Location.java
Normal file
10
corefx/src/main/java/com/core/data/Location.java
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
package com.core.data;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class Location {
|
||||||
|
private Double latitude = 0.0;
|
||||||
|
private Double longitude = 0.0;
|
||||||
|
private Double altitude = 0.0;
|
||||||
|
}
|
10
corefx/src/main/java/com/core/data/LocationConfig.java
Normal file
10
corefx/src/main/java/com/core/data/LocationConfig.java
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
package com.core.data;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class LocationConfig {
|
||||||
|
private Position position = new Position();
|
||||||
|
private Location location = new Location();
|
||||||
|
private Double scale;
|
||||||
|
}
|
36
corefx/src/main/java/com/core/data/MessageFlags.java
Normal file
36
corefx/src/main/java/com/core/data/MessageFlags.java
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
package com.core.data;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public enum MessageFlags {
|
||||||
|
ADD(1),
|
||||||
|
DELETE(2),
|
||||||
|
CRI(4),
|
||||||
|
LOCAL(8),
|
||||||
|
STRING(16),
|
||||||
|
TEXT(32),
|
||||||
|
TTY(64);
|
||||||
|
|
||||||
|
private static final Map<Integer, MessageFlags> LOOKUP = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (MessageFlags state : MessageFlags.values()) {
|
||||||
|
LOOKUP.put(state.getValue(), state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
MessageFlags(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MessageFlags get(int value) {
|
||||||
|
return LOOKUP.get(value);
|
||||||
|
}
|
||||||
|
}
|
25
corefx/src/main/java/com/core/data/MobilityConfig.java
Normal file
25
corefx/src/main/java/com/core/data/MobilityConfig.java
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package com.core.data;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class MobilityConfig {
|
||||||
|
private String file;
|
||||||
|
@JsonIgnore
|
||||||
|
private File scriptFile;
|
||||||
|
@JsonProperty("refresh_ms")
|
||||||
|
private Integer refresh;
|
||||||
|
private String loop;
|
||||||
|
private String autostart;
|
||||||
|
private String map;
|
||||||
|
@JsonProperty("script_start")
|
||||||
|
private String startScript;
|
||||||
|
@JsonProperty("script_pause")
|
||||||
|
private String pauseScript;
|
||||||
|
@JsonProperty("script_stop")
|
||||||
|
private String stopScript;
|
||||||
|
}
|
86
corefx/src/main/java/com/core/data/NodeType.java
Normal file
86
corefx/src/main/java/com/core/data/NodeType.java
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
package com.core.data;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
|
||||||
|
public class NodeType {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private static final AtomicInteger idGenerator = new AtomicInteger(0);
|
||||||
|
private static final Map<Integer, NodeType> ID_LOOKUP = new HashMap<>();
|
||||||
|
public static final int DEFAULT = 0;
|
||||||
|
public static final int SWITCH = 4;
|
||||||
|
public static final int HUB = 5;
|
||||||
|
public static final int WLAN = 6;
|
||||||
|
public static final int EMANE = 10;
|
||||||
|
@EqualsAndHashCode.Include
|
||||||
|
private final int id;
|
||||||
|
private final int value;
|
||||||
|
private final Set<String> services = new TreeSet<>();
|
||||||
|
private String display;
|
||||||
|
private String model;
|
||||||
|
private String icon;
|
||||||
|
|
||||||
|
// PHYSICAL = 1
|
||||||
|
// RJ45 = 7
|
||||||
|
// TUNNEL = 8
|
||||||
|
// KTUNNEL = 9
|
||||||
|
// EMANE = 10
|
||||||
|
// TAP_BRIDGE = 11
|
||||||
|
// PEER_TO_PEER = 12
|
||||||
|
// CONTROL_NET = 13
|
||||||
|
// EMANE_NET = 14;
|
||||||
|
|
||||||
|
static {
|
||||||
|
add(new NodeType(SWITCH, "lanswitch", "Switch", "/icons/switch-100.png"));
|
||||||
|
add(new NodeType(HUB, "hub", "Hub", "/icons/hub-100.png"));
|
||||||
|
add(new NodeType(WLAN, "wlan", "WLAN", "/icons/wlan-100.png"));
|
||||||
|
add(new NodeType(EMANE, "wlan", "EMANE", "/icons/emane-100.png"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public NodeType(int value, String model, String display, String icon) {
|
||||||
|
this.id = idGenerator.incrementAndGet();
|
||||||
|
this.value = value;
|
||||||
|
this.model = model;
|
||||||
|
this.display = display;
|
||||||
|
this.icon = icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void add(NodeType nodeType) {
|
||||||
|
ID_LOOKUP.put(nodeType.getId(), nodeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void remove(NodeType nodeType) {
|
||||||
|
ID_LOOKUP.remove(nodeType.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NodeType get(Integer id) {
|
||||||
|
return ID_LOOKUP.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Collection<NodeType> getAll() {
|
||||||
|
return ID_LOOKUP.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NodeType find(Integer type, String model) {
|
||||||
|
return ID_LOOKUP.values().stream()
|
||||||
|
.filter(nodeType -> {
|
||||||
|
boolean sameType = nodeType.getValue() == type;
|
||||||
|
boolean sameModel;
|
||||||
|
if (model != null) {
|
||||||
|
sameModel = model.equals(nodeType.getModel());
|
||||||
|
} else {
|
||||||
|
sameModel = nodeType.getModel() == null;
|
||||||
|
}
|
||||||
|
return sameType && sameModel;
|
||||||
|
})
|
||||||
|
.findFirst().orElse(null);
|
||||||
|
}
|
||||||
|
}
|
12
corefx/src/main/java/com/core/data/Position.java
Normal file
12
corefx/src/main/java/com/core/data/Position.java
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package com.core.data;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class Position {
|
||||||
|
private Double x;
|
||||||
|
private Double y;
|
||||||
|
private Double z;
|
||||||
|
}
|
15
corefx/src/main/java/com/core/data/Session.java
Normal file
15
corefx/src/main/java/com/core/data/Session.java
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package com.core.data;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class Session {
|
||||||
|
private Integer state;
|
||||||
|
private List<CoreNode> nodes = new ArrayList<>();
|
||||||
|
private List<CoreLink> links = new ArrayList<>();
|
||||||
|
}
|
13
corefx/src/main/java/com/core/data/SessionOverview.java
Normal file
13
corefx/src/main/java/com/core/data/SessionOverview.java
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package com.core.data;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class SessionOverview {
|
||||||
|
private Integer id;
|
||||||
|
private Integer state;
|
||||||
|
private Integer nodes = 0;
|
||||||
|
private String url;
|
||||||
|
}
|
38
corefx/src/main/java/com/core/data/SessionState.java
Normal file
38
corefx/src/main/java/com/core/data/SessionState.java
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package com.core.data;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public enum SessionState {
|
||||||
|
DEFINITION(1),
|
||||||
|
CONFIGURATION(2),
|
||||||
|
INSTANTIATION(3),
|
||||||
|
RUNTIME(4),
|
||||||
|
DATA_COLLECT(5),
|
||||||
|
SHUTDOWN(6),
|
||||||
|
START(7),
|
||||||
|
STOP(8),
|
||||||
|
PAUSE(9);
|
||||||
|
|
||||||
|
private static final Map<Integer, SessionState> LOOKUP = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (SessionState state : SessionState.values()) {
|
||||||
|
LOOKUP.put(state.getValue(), state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
SessionState(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SessionState get(int value) {
|
||||||
|
return LOOKUP.get(value);
|
||||||
|
}
|
||||||
|
}
|
12
corefx/src/main/java/com/core/data/Throughputs.java
Normal file
12
corefx/src/main/java/com/core/data/Throughputs.java
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package com.core.data;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class Throughputs {
|
||||||
|
private List<InterfaceThroughput> interfaces = new ArrayList<>();
|
||||||
|
private List<BridgeThroughput> bridges = new ArrayList<>();
|
||||||
|
}
|
11
corefx/src/main/java/com/core/datavis/CoreGraph.java
Normal file
11
corefx/src/main/java/com/core/datavis/CoreGraph.java
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package com.core.datavis;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class CoreGraph {
|
||||||
|
private String title;
|
||||||
|
private CoreGraphAxis xAxis;
|
||||||
|
private CoreGraphAxis yAxis;
|
||||||
|
private GraphType graphType;
|
||||||
|
}
|
15
corefx/src/main/java/com/core/datavis/CoreGraphAxis.java
Normal file
15
corefx/src/main/java/com/core/datavis/CoreGraphAxis.java
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package com.core.datavis;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class CoreGraphAxis {
|
||||||
|
private String label;
|
||||||
|
private Double lower;
|
||||||
|
private Double upper;
|
||||||
|
private Double tick;
|
||||||
|
}
|
15
corefx/src/main/java/com/core/datavis/CoreGraphData.java
Normal file
15
corefx/src/main/java/com/core/datavis/CoreGraphData.java
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package com.core.datavis;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class CoreGraphData {
|
||||||
|
private String name;
|
||||||
|
private Double x;
|
||||||
|
private Double y;
|
||||||
|
private Double weight;
|
||||||
|
}
|
157
corefx/src/main/java/com/core/datavis/CoreGraphWrapper.java
Normal file
157
corefx/src/main/java/com/core/datavis/CoreGraphWrapper.java
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
package com.core.datavis;
|
||||||
|
|
||||||
|
import javafx.scene.chart.*;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
public class CoreGraphWrapper {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private final GraphType graphType;
|
||||||
|
private PieChart pieChart;
|
||||||
|
private final Map<String, PieChart.Data> pieData = new HashMap<>();
|
||||||
|
private BarChart<String, Number> barChart;
|
||||||
|
private final Map<String, XYChart.Data<String, Number>> barMap = new HashMap<>();
|
||||||
|
private XYChart<Number, Number> xyChart;
|
||||||
|
private final XYChart.Series<Number, Number> series = new XYChart.Series<>();
|
||||||
|
private final XYChart.Series<String, Number> barSeries = new XYChart.Series<>();
|
||||||
|
private AtomicInteger timeValue = new AtomicInteger(0);
|
||||||
|
|
||||||
|
public CoreGraphWrapper(CoreGraph coreGraph) {
|
||||||
|
graphType = coreGraph.getGraphType();
|
||||||
|
createChart(coreGraph);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Chart getChart() {
|
||||||
|
switch (graphType) {
|
||||||
|
case PIE:
|
||||||
|
return pieChart;
|
||||||
|
case BAR:
|
||||||
|
return barChart;
|
||||||
|
default:
|
||||||
|
return xyChart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(CoreGraphData coreGraphData) {
|
||||||
|
switch (graphType) {
|
||||||
|
case PIE:
|
||||||
|
case BAR:
|
||||||
|
add(coreGraphData.getName(), coreGraphData.getY());
|
||||||
|
break;
|
||||||
|
case TIME:
|
||||||
|
add(coreGraphData.getY());
|
||||||
|
break;
|
||||||
|
case BUBBLE:
|
||||||
|
add(coreGraphData.getX(), coreGraphData.getY(), coreGraphData.getWeight());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
add(coreGraphData.getX(), coreGraphData.getY());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(String name, double value) {
|
||||||
|
if (GraphType.PIE == graphType) {
|
||||||
|
PieChart.Data data = pieData.computeIfAbsent(name, x -> {
|
||||||
|
PieChart.Data newData = new PieChart.Data(x, value);
|
||||||
|
pieChart.getData().add(newData);
|
||||||
|
return newData;
|
||||||
|
});
|
||||||
|
data.setPieValue(value);
|
||||||
|
} else {
|
||||||
|
XYChart.Data<String, Number> data = barMap.computeIfAbsent(name, x -> {
|
||||||
|
XYChart.Data<String, Number> newData = new XYChart.Data<>(name, value);
|
||||||
|
barSeries.getData().add(newData);
|
||||||
|
return newData;
|
||||||
|
});
|
||||||
|
data.setYValue(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Number y) {
|
||||||
|
series.getData().add(new XYChart.Data<>(timeValue.getAndIncrement(), y));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Number x, Number y) {
|
||||||
|
series.getData().add(new XYChart.Data<>(x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Number x, Number y, Number weight) {
|
||||||
|
series.getData().add(new XYChart.Data<>(x, y, weight));
|
||||||
|
}
|
||||||
|
|
||||||
|
private NumberAxis getAxis(CoreGraphAxis graphAxis) {
|
||||||
|
return new NumberAxis(graphAxis.getLabel(), graphAxis.getLower(),
|
||||||
|
graphAxis.getUpper(), graphAxis.getTick());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createChart(CoreGraph coreGraph) {
|
||||||
|
NumberAxis xAxis;
|
||||||
|
NumberAxis yAxis;
|
||||||
|
|
||||||
|
switch (coreGraph.getGraphType()) {
|
||||||
|
case AREA:
|
||||||
|
xAxis = getAxis(coreGraph.getXAxis());
|
||||||
|
yAxis = getAxis(coreGraph.getYAxis());
|
||||||
|
xyChart = new AreaChart<>(xAxis, yAxis);
|
||||||
|
xyChart.setTitle(coreGraph.getTitle());
|
||||||
|
xyChart.setLegendVisible(false);
|
||||||
|
xyChart.getData().add(series);
|
||||||
|
break;
|
||||||
|
case TIME:
|
||||||
|
xAxis = new NumberAxis();
|
||||||
|
xAxis.setLabel(coreGraph.getXAxis().getLabel());
|
||||||
|
xAxis.setTickUnit(1);
|
||||||
|
xAxis.setLowerBound(0);
|
||||||
|
yAxis = getAxis(coreGraph.getYAxis());
|
||||||
|
xyChart = new LineChart<>(xAxis, yAxis);
|
||||||
|
xyChart.setTitle(coreGraph.getTitle());
|
||||||
|
xyChart.setLegendVisible(false);
|
||||||
|
xyChart.getData().add(series);
|
||||||
|
break;
|
||||||
|
case LINE:
|
||||||
|
xAxis = getAxis(coreGraph.getXAxis());
|
||||||
|
yAxis = getAxis(coreGraph.getYAxis());
|
||||||
|
xyChart = new LineChart<>(xAxis, yAxis);
|
||||||
|
xyChart.setTitle(coreGraph.getTitle());
|
||||||
|
xyChart.setLegendVisible(false);
|
||||||
|
xyChart.getData().add(series);
|
||||||
|
break;
|
||||||
|
case BUBBLE:
|
||||||
|
xAxis = getAxis(coreGraph.getXAxis());
|
||||||
|
yAxis = getAxis(coreGraph.getYAxis());
|
||||||
|
xyChart = new BubbleChart<>(xAxis, yAxis);
|
||||||
|
xyChart.setTitle(coreGraph.getTitle());
|
||||||
|
xyChart.setLegendVisible(false);
|
||||||
|
xyChart.getData().add(series);
|
||||||
|
break;
|
||||||
|
case SCATTER:
|
||||||
|
xAxis = getAxis(coreGraph.getXAxis());
|
||||||
|
yAxis = getAxis(coreGraph.getYAxis());
|
||||||
|
xyChart = new ScatterChart<>(xAxis, yAxis);
|
||||||
|
xyChart.setTitle(coreGraph.getTitle());
|
||||||
|
xyChart.setLegendVisible(false);
|
||||||
|
xyChart.getData().add(series);
|
||||||
|
break;
|
||||||
|
case PIE:
|
||||||
|
pieChart = new PieChart();
|
||||||
|
pieChart.setTitle(coreGraph.getTitle());
|
||||||
|
break;
|
||||||
|
case BAR:
|
||||||
|
CategoryAxis categoryAxis = new CategoryAxis();
|
||||||
|
categoryAxis.setLabel(coreGraph.getXAxis().getLabel());
|
||||||
|
yAxis = getAxis(coreGraph.getYAxis());
|
||||||
|
barChart = new BarChart<>(categoryAxis, yAxis);
|
||||||
|
barChart.setLegendVisible(false);
|
||||||
|
barChart.setTitle(coreGraph.getTitle());
|
||||||
|
barChart.getData().add(barSeries);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException(String.format("unknown graph type: %s",
|
||||||
|
coreGraph.getGraphType()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
corefx/src/main/java/com/core/datavis/GraphType.java
Normal file
11
corefx/src/main/java/com/core/datavis/GraphType.java
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package com.core.datavis;
|
||||||
|
|
||||||
|
public enum GraphType {
|
||||||
|
PIE,
|
||||||
|
LINE,
|
||||||
|
TIME,
|
||||||
|
AREA,
|
||||||
|
BAR,
|
||||||
|
SCATTER,
|
||||||
|
BUBBLE
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.core.graph;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.data.CoreNode;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.event.EventHandler;
|
||||||
|
import javafx.scene.control.MenuItem;
|
||||||
|
|
||||||
|
abstract class AbstractNodeContextMenu extends GraphContextMenu {
|
||||||
|
final CoreNode coreNode;
|
||||||
|
|
||||||
|
AbstractNodeContextMenu(Controller controller, CoreNode coreNode) {
|
||||||
|
super(controller);
|
||||||
|
this.coreNode = coreNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addMenuItem(String text, EventHandler<ActionEvent> handler) {
|
||||||
|
MenuItem menuItem = new MenuItem(text);
|
||||||
|
menuItem.setOnAction(handler);
|
||||||
|
getItems().add(menuItem);
|
||||||
|
}
|
||||||
|
}
|
51
corefx/src/main/java/com/core/graph/BackgroundPaintable.java
Normal file
51
corefx/src/main/java/com/core/graph/BackgroundPaintable.java
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package com.core.graph;
|
||||||
|
|
||||||
|
import edu.uci.ics.jung.visualization.Layer;
|
||||||
|
import edu.uci.ics.jung.visualization.VisualizationViewer;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.geom.AffineTransform;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
public class BackgroundPaintable<V, E> implements VisualizationViewer.Paintable {
|
||||||
|
private final ImageIcon imageIcon;
|
||||||
|
private final VisualizationViewer<V, E> vv;
|
||||||
|
private final String imagePath;
|
||||||
|
|
||||||
|
public BackgroundPaintable(String imagePath, VisualizationViewer<V, E> vv) throws IOException {
|
||||||
|
this.imagePath = imagePath;
|
||||||
|
Image image = ImageIO.read(Paths.get(imagePath).toFile());
|
||||||
|
imageIcon = new ImageIcon(image);
|
||||||
|
this.vv = vv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getImage() {
|
||||||
|
return imagePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paint(Graphics g) {
|
||||||
|
Graphics2D g2d = (Graphics2D) g;
|
||||||
|
AffineTransform oldXform = g2d.getTransform();
|
||||||
|
AffineTransform lat =
|
||||||
|
vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).getTransform();
|
||||||
|
AffineTransform vat =
|
||||||
|
vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW).getTransform();
|
||||||
|
AffineTransform at = new AffineTransform();
|
||||||
|
at.concatenate(g2d.getTransform());
|
||||||
|
at.concatenate(vat);
|
||||||
|
at.concatenate(lat);
|
||||||
|
g2d.setTransform(at);
|
||||||
|
g.drawImage(imageIcon.getImage(), 0, 0,
|
||||||
|
imageIcon.getIconWidth(), imageIcon.getIconHeight(), vv);
|
||||||
|
g2d.setTransform(oldXform);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean useTransform() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
41
corefx/src/main/java/com/core/graph/CoreAddresses.java
Normal file
41
corefx/src/main/java/com/core/graph/CoreAddresses.java
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package com.core.graph;
|
||||||
|
|
||||||
|
import com.core.data.CoreInterface;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
public class CoreAddresses {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
public static final int IP4_MASK = 24;
|
||||||
|
public static final int IP4_INDEX = (IP4_MASK / 8) - 1;
|
||||||
|
|
||||||
|
private String ip4Base;
|
||||||
|
|
||||||
|
public CoreAddresses(String ip4Base) {
|
||||||
|
this.ip4Base = ip4Base;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSubnet(Collection<CoreInterface> nodeOneInterfaces, Collection<CoreInterface> nodeTwoInterfaces) {
|
||||||
|
int subOne = getMaxSubnet(nodeOneInterfaces);
|
||||||
|
int subTwo = getMaxSubnet(nodeTwoInterfaces);
|
||||||
|
logger.info("next subnet: {} - {}", subOne, subTwo);
|
||||||
|
return Math.max(subOne, subTwo) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getMaxSubnet(Collection<CoreInterface> coreInterfaces) {
|
||||||
|
int sub = 0;
|
||||||
|
for (CoreInterface coreInterface : coreInterfaces) {
|
||||||
|
String[] values = coreInterface.getIp4().split("\\.");
|
||||||
|
int currentSub = Integer.parseInt(values[IP4_INDEX]);
|
||||||
|
logger.info("checking {} value {}", coreInterface.getIp4(), currentSub);
|
||||||
|
sub = Math.max(currentSub, sub);
|
||||||
|
}
|
||||||
|
return sub;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIp4Address(int sub, int id) {
|
||||||
|
return String.format("%s.%s.%s", ip4Base, sub, id);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package com.core.graph;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import edu.uci.ics.jung.visualization.RenderContext;
|
||||||
|
import edu.uci.ics.jung.visualization.VisualizationViewer;
|
||||||
|
import edu.uci.ics.jung.visualization.annotations.AnnotatingGraphMousePlugin;
|
||||||
|
import edu.uci.ics.jung.visualization.annotations.Annotation;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.geom.Point2D;
|
||||||
|
import java.awt.geom.RectangularShape;
|
||||||
|
|
||||||
|
public class CoreAnnotatingGraphMousePlugin<V, E> extends AnnotatingGraphMousePlugin<V, E> {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private final Controller controller;
|
||||||
|
private JFrame frame = new JFrame();
|
||||||
|
|
||||||
|
public CoreAnnotatingGraphMousePlugin(Controller controller, RenderContext<V, E> renderContext) {
|
||||||
|
super(renderContext);
|
||||||
|
this.controller = controller;
|
||||||
|
frame.setVisible(false);
|
||||||
|
frame.setAlwaysOnTop(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseReleased(MouseEvent e) {
|
||||||
|
VisualizationViewer<V, E> vv = (VisualizationViewer) e.getSource();
|
||||||
|
if (e.isPopupTrigger()) {
|
||||||
|
frame.setLocationRelativeTo(vv);
|
||||||
|
String annotationString = JOptionPane.showInputDialog(frame, "Annotation:",
|
||||||
|
"Annotation Label", JOptionPane.PLAIN_MESSAGE);
|
||||||
|
if (annotationString != null && annotationString.length() > 0) {
|
||||||
|
Point2D p = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(this.down);
|
||||||
|
Annotation<String> annotation = new Annotation(annotationString, this.layer,
|
||||||
|
this.annotationColor, this.fill, p);
|
||||||
|
this.annotationManager.add(this.layer, annotation);
|
||||||
|
}
|
||||||
|
} else if (e.getModifiers() == this.modifiers && this.down != null) {
|
||||||
|
Point2D out = e.getPoint();
|
||||||
|
RectangularShape arect = (RectangularShape) this.rectangularShape.clone();
|
||||||
|
arect.setFrameFromDiagonal(this.down, out);
|
||||||
|
Shape s = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(arect);
|
||||||
|
Annotation<Shape> annotation = new Annotation(s, this.layer, this.annotationColor, this.fill, out);
|
||||||
|
this.annotationManager.add(this.layer, annotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.down = null;
|
||||||
|
vv.removePostRenderPaintable(this.lensPaintable);
|
||||||
|
vv.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.core.graph;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.google.common.base.Supplier;
|
||||||
|
import edu.uci.ics.jung.visualization.RenderContext;
|
||||||
|
import edu.uci.ics.jung.visualization.control.EditingModalGraphMouse;
|
||||||
|
|
||||||
|
public class CoreEditingModalGraphMouse<V, E> extends EditingModalGraphMouse<V, E> {
|
||||||
|
public CoreEditingModalGraphMouse(Controller controller, NetworkGraph networkGraph,
|
||||||
|
RenderContext<V, E> rc, Supplier<V> vertexFactory, Supplier<E> edgeFactory) {
|
||||||
|
super(rc, vertexFactory, edgeFactory);
|
||||||
|
remove(annotatingPlugin);
|
||||||
|
remove(popupEditingPlugin);
|
||||||
|
annotatingPlugin = new CoreAnnotatingGraphMousePlugin<>(controller, rc);
|
||||||
|
popupEditingPlugin = new CorePopupGraphMousePlugin<>(controller, networkGraph, vertexFactory, edgeFactory);
|
||||||
|
}
|
||||||
|
}
|
31
corefx/src/main/java/com/core/graph/CoreObservableGraph.java
Normal file
31
corefx/src/main/java/com/core/graph/CoreObservableGraph.java
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package com.core.graph;
|
||||||
|
|
||||||
|
import edu.uci.ics.jung.graph.Graph;
|
||||||
|
import edu.uci.ics.jung.graph.ObservableGraph;
|
||||||
|
import edu.uci.ics.jung.graph.util.EdgeType;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
public class CoreObservableGraph<V, E> extends ObservableGraph<V, E> {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
|
||||||
|
public CoreObservableGraph(Graph<V, E> graph) {
|
||||||
|
super(graph);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addEdge(E e, V v1, V v2, EdgeType edgeType) {
|
||||||
|
if (v1 == null || v2 == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return super.addEdge(e, v1, v2, edgeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addEdge(E e, V v1, V v2) {
|
||||||
|
if (v1 == null || v2 == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return super.addEdge(e, v1, v2);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
package com.core.graph;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.data.CoreLink;
|
||||||
|
import com.core.data.CoreNode;
|
||||||
|
import com.core.data.NodeType;
|
||||||
|
import com.google.common.base.Supplier;
|
||||||
|
import edu.uci.ics.jung.algorithms.layout.GraphElementAccessor;
|
||||||
|
import edu.uci.ics.jung.algorithms.layout.Layout;
|
||||||
|
import edu.uci.ics.jung.visualization.control.EditingPopupGraphMousePlugin;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.scene.control.ContextMenu;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.geom.Point2D;
|
||||||
|
|
||||||
|
public class CorePopupGraphMousePlugin<V, E> extends EditingPopupGraphMousePlugin<V, E> {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private final Controller controller;
|
||||||
|
private final NetworkGraph networkGraph;
|
||||||
|
private final Layout<CoreNode, CoreLink> graphLayout;
|
||||||
|
private final GraphElementAccessor<CoreNode, CoreLink> pickSupport;
|
||||||
|
|
||||||
|
public CorePopupGraphMousePlugin(Controller controller, NetworkGraph networkGraph,
|
||||||
|
Supplier<V> vertexFactory, Supplier<E> edgeFactory) {
|
||||||
|
super(vertexFactory, edgeFactory);
|
||||||
|
this.controller = controller;
|
||||||
|
this.networkGraph = networkGraph;
|
||||||
|
graphLayout = this.networkGraph.getGraphLayout();
|
||||||
|
pickSupport = this.networkGraph.getGraphViewer().getPickSupport();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void handlePopup(MouseEvent e) {
|
||||||
|
logger.info("showing popup!");
|
||||||
|
final Point2D p = e.getPoint();
|
||||||
|
|
||||||
|
final CoreNode node = pickSupport.getVertex(graphLayout, p.getX(), p.getY());
|
||||||
|
final CoreLink link = pickSupport.getEdge(graphLayout, p.getX(), p.getY());
|
||||||
|
|
||||||
|
final ContextMenu contextMenu;
|
||||||
|
if (node != null) {
|
||||||
|
contextMenu = handleNodeContext(node);
|
||||||
|
} else if (link != null) {
|
||||||
|
contextMenu = new LinkContextMenu(controller, link);
|
||||||
|
} else {
|
||||||
|
contextMenu = new ContextMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!contextMenu.getItems().isEmpty()) {
|
||||||
|
logger.info("showing context menu");
|
||||||
|
Platform.runLater(() -> contextMenu.show(controller.getWindow(),
|
||||||
|
e.getXOnScreen(), e.getYOnScreen()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ContextMenu handleNodeContext(final CoreNode node) {
|
||||||
|
ContextMenu contextMenu = new ContextMenu();
|
||||||
|
switch (node.getType()) {
|
||||||
|
case NodeType.DEFAULT:
|
||||||
|
contextMenu = new NodeContextMenu(controller, node);
|
||||||
|
break;
|
||||||
|
case NodeType.WLAN:
|
||||||
|
contextMenu = new WlanContextMenu(controller, node);
|
||||||
|
break;
|
||||||
|
case NodeType.EMANE:
|
||||||
|
contextMenu = new EmaneContextMenu(controller, node);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
logger.warn("no context menu for node: {}", node.getType());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return contextMenu;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
package com.core.graph;
|
||||||
|
|
||||||
|
import edu.uci.ics.jung.visualization.renderers.DefaultVertexLabelRenderer;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.border.EmptyBorder;
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
public class CoreVertexLabelRenderer extends DefaultVertexLabelRenderer {
|
||||||
|
private Color foregroundColor = Color.WHITE;
|
||||||
|
private Color backgroundColor = Color.BLACK;
|
||||||
|
|
||||||
|
CoreVertexLabelRenderer() {
|
||||||
|
super(Color.YELLOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setColors(Color foregroundColor, Color backgroundColor) {
|
||||||
|
this.foregroundColor = foregroundColor;
|
||||||
|
this.backgroundColor = backgroundColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <V> Component getVertexLabelRendererComponent(JComponent vv, Object value, Font font, boolean isSelected, V vertex) {
|
||||||
|
super.setForeground(foregroundColor);
|
||||||
|
if (isSelected) {
|
||||||
|
this.setForeground(this.pickedVertexLabelColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
super.setBackground(backgroundColor);
|
||||||
|
if (font != null) {
|
||||||
|
this.setFont(font);
|
||||||
|
} else {
|
||||||
|
this.setFont(vv.getFont());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setIcon(null);
|
||||||
|
EmptyBorder padding = new EmptyBorder(5, 5, 5, 5);
|
||||||
|
this.setBorder(padding);
|
||||||
|
this.setValue(value);
|
||||||
|
setFont(getFont().deriveFont(Font.BOLD));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
26
corefx/src/main/java/com/core/graph/EmaneContextMenu.java
Normal file
26
corefx/src/main/java/com/core/graph/EmaneContextMenu.java
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package com.core.graph;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.data.CoreNode;
|
||||||
|
import com.core.ui.dialogs.MobilityPlayerDialog;
|
||||||
|
|
||||||
|
class EmaneContextMenu extends AbstractNodeContextMenu {
|
||||||
|
EmaneContextMenu(Controller controller, CoreNode coreNode) {
|
||||||
|
super(controller, coreNode);
|
||||||
|
setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setup() {
|
||||||
|
addMenuItem("EMANE Settings", event -> controller.getNodeEmaneDialog().showDialog(coreNode));
|
||||||
|
if (controller.getCoreClient().isRunning()) {
|
||||||
|
MobilityPlayerDialog mobilityPlayerDialog = controller.getMobilityPlayerDialogs().get(coreNode.getId());
|
||||||
|
if (mobilityPlayerDialog != null && !mobilityPlayerDialog.getStage().isShowing()) {
|
||||||
|
addMenuItem("Mobility Script", event -> mobilityPlayerDialog.show());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addMenuItem("Mobility", event -> controller.getMobilityDialog().showDialog(coreNode));
|
||||||
|
addMenuItem("Link MDRs", event -> controller.getNetworkGraph().linkMdrs(coreNode));
|
||||||
|
addMenuItem("Delete Node", event -> controller.deleteNode(coreNode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
corefx/src/main/java/com/core/graph/GraphContextMenu.java
Normal file
22
corefx/src/main/java/com/core/graph/GraphContextMenu.java
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package com.core.graph;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.event.EventHandler;
|
||||||
|
import javafx.scene.control.ContextMenu;
|
||||||
|
import javafx.scene.control.MenuItem;
|
||||||
|
|
||||||
|
abstract class GraphContextMenu extends ContextMenu {
|
||||||
|
final Controller controller;
|
||||||
|
|
||||||
|
GraphContextMenu(Controller controller) {
|
||||||
|
super();
|
||||||
|
this.controller = controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addMenuItem(String text, EventHandler<ActionEvent> handler) {
|
||||||
|
MenuItem menuItem = new MenuItem(text);
|
||||||
|
menuItem.setOnAction(handler);
|
||||||
|
getItems().add(menuItem);
|
||||||
|
}
|
||||||
|
}
|
21
corefx/src/main/java/com/core/graph/LinkContextMenu.java
Normal file
21
corefx/src/main/java/com/core/graph/LinkContextMenu.java
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package com.core.graph;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.data.CoreLink;
|
||||||
|
|
||||||
|
class LinkContextMenu extends GraphContextMenu {
|
||||||
|
final CoreLink coreLink;
|
||||||
|
|
||||||
|
LinkContextMenu(Controller controller, CoreLink coreLink) {
|
||||||
|
super(controller);
|
||||||
|
this.coreLink = coreLink;
|
||||||
|
setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setup() {
|
||||||
|
if (!controller.getCoreClient().isRunning()) {
|
||||||
|
addMenuItem("Delete Link",
|
||||||
|
event -> controller.getNetworkGraph().removeLink(coreLink));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
492
corefx/src/main/java/com/core/graph/NetworkGraph.java
Normal file
492
corefx/src/main/java/com/core/graph/NetworkGraph.java
Normal file
|
@ -0,0 +1,492 @@
|
||||||
|
package com.core.graph;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.data.*;
|
||||||
|
import com.core.ui.Toast;
|
||||||
|
import com.core.ui.dialogs.TerminalDialog;
|
||||||
|
import com.core.utils.Configuration;
|
||||||
|
import com.core.utils.IconUtils;
|
||||||
|
import com.google.common.base.Supplier;
|
||||||
|
import edu.uci.ics.jung.algorithms.layout.StaticLayout;
|
||||||
|
import edu.uci.ics.jung.graph.ObservableGraph;
|
||||||
|
import edu.uci.ics.jung.graph.event.GraphEvent;
|
||||||
|
import edu.uci.ics.jung.graph.event.GraphEventListener;
|
||||||
|
import edu.uci.ics.jung.graph.util.Pair;
|
||||||
|
import edu.uci.ics.jung.visualization.RenderContext;
|
||||||
|
import edu.uci.ics.jung.visualization.VisualizationViewer;
|
||||||
|
import edu.uci.ics.jung.visualization.annotations.AnnotationControls;
|
||||||
|
import edu.uci.ics.jung.visualization.control.EditingModalGraphMouse;
|
||||||
|
import edu.uci.ics.jung.visualization.control.GraphMouseListener;
|
||||||
|
import edu.uci.ics.jung.visualization.control.ModalGraphMouse;
|
||||||
|
import edu.uci.ics.jung.visualization.decorators.EdgeShape;
|
||||||
|
import edu.uci.ics.jung.visualization.renderers.Renderer;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.apache.commons.net.util.SubnetUtils;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.geom.Ellipse2D;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class NetworkGraph {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private static final int EDGE_LABEL_OFFSET = -5;
|
||||||
|
private static final int EDGE_WIDTH = 5;
|
||||||
|
private Controller controller;
|
||||||
|
private ObservableGraph<CoreNode, CoreLink> graph;
|
||||||
|
private StaticLayout<CoreNode, CoreLink> graphLayout;
|
||||||
|
private VisualizationViewer<CoreNode, CoreLink> graphViewer;
|
||||||
|
private EditingModalGraphMouse<CoreNode, CoreLink> graphMouse;
|
||||||
|
private AnnotationControls<CoreNode, CoreLink> annotationControls;
|
||||||
|
|
||||||
|
private SubnetUtils subnetUtils = new SubnetUtils("10.0.0.0/24");
|
||||||
|
private CoreAddresses coreAddresses = new CoreAddresses("10.0");
|
||||||
|
private NodeType nodeType;
|
||||||
|
private Map<Integer, CoreNode> nodeMap = new ConcurrentHashMap<>();
|
||||||
|
private int vertexId = 1;
|
||||||
|
private int linkId = 1;
|
||||||
|
private Supplier<CoreNode> vertexFactory = () -> new CoreNode(vertexId++);
|
||||||
|
private Supplier<CoreLink> linkFactory = () -> new CoreLink(linkId++);
|
||||||
|
private CorePopupGraphMousePlugin customPopupPlugin;
|
||||||
|
private CoreAnnotatingGraphMousePlugin<CoreNode, CoreLink> customAnnotatingPlugin;
|
||||||
|
private BackgroundPaintable<CoreNode, CoreLink> backgroundPaintable;
|
||||||
|
private CoreVertexLabelRenderer nodeLabelRenderer = new CoreVertexLabelRenderer();
|
||||||
|
|
||||||
|
// display options
|
||||||
|
private boolean showThroughput = false;
|
||||||
|
private Double throughputLimit = null;
|
||||||
|
private int throughputWidth = 10;
|
||||||
|
|
||||||
|
public NetworkGraph(Controller controller) {
|
||||||
|
this.controller = controller;
|
||||||
|
graph = new CoreObservableGraph<>(new UndirectedSimpleGraph<>());
|
||||||
|
graph.addGraphEventListener(graphEventListener);
|
||||||
|
graphLayout = new StaticLayout<>(graph);
|
||||||
|
graphViewer = new VisualizationViewer<>(graphLayout);
|
||||||
|
graphViewer.setBackground(Color.WHITE);
|
||||||
|
graphViewer.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.S);
|
||||||
|
|
||||||
|
RenderContext<CoreNode, CoreLink> renderContext = graphViewer.getRenderContext();
|
||||||
|
|
||||||
|
// node render properties
|
||||||
|
renderContext.setVertexLabelTransformer(CoreNode::getName);
|
||||||
|
renderContext.setVertexLabelRenderer(nodeLabelRenderer);
|
||||||
|
renderContext.setVertexShapeTransformer(node -> {
|
||||||
|
double offset = -(IconUtils.ICON_SIZE / 2.0);
|
||||||
|
return new Ellipse2D.Double(offset, offset, IconUtils.ICON_SIZE, IconUtils.ICON_SIZE);
|
||||||
|
});
|
||||||
|
renderContext.setVertexIconTransformer(vertex -> {
|
||||||
|
long wirelessLinks = wirelessLinkCount(vertex);
|
||||||
|
vertex.getRadioIcon().setWiressLinks(wirelessLinks);
|
||||||
|
return vertex.getGraphIcon();
|
||||||
|
});
|
||||||
|
|
||||||
|
// link render properties
|
||||||
|
renderContext.setEdgeLabelTransformer(edge -> {
|
||||||
|
if (!showThroughput || edge == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
double kbps = edge.getThroughput() / 1000.0;
|
||||||
|
return String.format("%.2f kbps", kbps);
|
||||||
|
});
|
||||||
|
renderContext.setLabelOffset(EDGE_LABEL_OFFSET);
|
||||||
|
renderContext.setEdgeStrokeTransformer(edge -> {
|
||||||
|
// determine edge width
|
||||||
|
int width = EDGE_WIDTH;
|
||||||
|
if (throughputLimit != null && edge.getThroughput() > throughputLimit) {
|
||||||
|
width = throughputWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
LinkTypes linkType = LinkTypes.get(edge.getType());
|
||||||
|
if (LinkTypes.WIRELESS == linkType) {
|
||||||
|
float[] dash = {15.0f};
|
||||||
|
return new BasicStroke(width, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND,
|
||||||
|
0, dash, 0);
|
||||||
|
} else {
|
||||||
|
return new BasicStroke(width);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
renderContext.setEdgeShapeTransformer(EdgeShape.line(graph));
|
||||||
|
renderContext.setEdgeDrawPaintTransformer(edge -> {
|
||||||
|
LinkTypes linkType = LinkTypes.get(edge.getType());
|
||||||
|
if (LinkTypes.WIRELESS == linkType) {
|
||||||
|
return Color.BLUE;
|
||||||
|
} else {
|
||||||
|
return Color.BLACK;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
renderContext.setEdgeIncludePredicate(predicate -> predicate.element.isVisible());
|
||||||
|
|
||||||
|
graphViewer.setVertexToolTipTransformer(renderContext.getVertexLabelTransformer());
|
||||||
|
graphMouse = new CoreEditingModalGraphMouse<>(controller, this, renderContext,
|
||||||
|
vertexFactory, linkFactory);
|
||||||
|
graphViewer.setGraphMouse(graphMouse);
|
||||||
|
|
||||||
|
// mouse events
|
||||||
|
graphViewer.addGraphMouseListener(new GraphMouseListener<CoreNode>() {
|
||||||
|
@Override
|
||||||
|
public void graphClicked(CoreNode node, MouseEvent mouseEvent) {
|
||||||
|
// double click
|
||||||
|
logger.info("click count: {}, running?: {}", mouseEvent.getClickCount(),
|
||||||
|
controller.getCoreClient().isRunning());
|
||||||
|
|
||||||
|
if (mouseEvent.getClickCount() == 2 && controller.getCoreClient().isRunning()) {
|
||||||
|
if (controller.getCoreClient().isLocalConnection()) {
|
||||||
|
try {
|
||||||
|
String shellCommand = controller.getConfiguration().getShellCommand();
|
||||||
|
String terminalCommand = controller.getCoreClient().getTerminalCommand(node);
|
||||||
|
terminalCommand = String.format("%s %s", shellCommand, terminalCommand);
|
||||||
|
logger.info("launching node terminal: {}", terminalCommand);
|
||||||
|
String[] commands = terminalCommand.split("\\s+");
|
||||||
|
logger.info("launching node terminal: {}", Arrays.toString(commands));
|
||||||
|
Process p = new ProcessBuilder(commands).start();
|
||||||
|
try {
|
||||||
|
if (!p.waitFor(5, TimeUnit.SECONDS)) {
|
||||||
|
Toast.error("Node terminal command failed");
|
||||||
|
}
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
logger.error("error waiting for terminal to start", ex);
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.error("error launching terminal", ex);
|
||||||
|
Toast.error("Node terminal failed to start");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
TerminalDialog terminalDialog = new TerminalDialog(controller);
|
||||||
|
terminalDialog.setOwner(controller.getWindow());
|
||||||
|
terminalDialog.showDialog(node);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void graphPressed(CoreNode node, MouseEvent mouseEvent) {
|
||||||
|
logger.debug("graph pressed: {} - {}", node, mouseEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void graphReleased(CoreNode node, MouseEvent mouseEvent) {
|
||||||
|
if (SwingUtilities.isLeftMouseButton(mouseEvent)) {
|
||||||
|
Double newX = graphLayout.getX(node);
|
||||||
|
Double newY = graphLayout.getY(node);
|
||||||
|
Double oldX = node.getPosition().getX();
|
||||||
|
Double oldY = node.getPosition().getY();
|
||||||
|
if (newX.equals(oldX) && newY.equals(oldY)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
logger.debug("graph moved node({}): {},{}", node.getName(), newX, newY);
|
||||||
|
node.getPosition().setX(newX);
|
||||||
|
node.getPosition().setY(newY);
|
||||||
|
|
||||||
|
// upate node when session is active
|
||||||
|
if (controller.getCoreClient().isRunning()) {
|
||||||
|
try {
|
||||||
|
controller.getCoreClient().editNode(node);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Toast.error("failed to update node location");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color convertJfxColor(String hexValue) {
|
||||||
|
javafx.scene.paint.Color color = javafx.scene.paint.Color.web(hexValue);
|
||||||
|
return new Color((float) color.getRed(), (float) color.getGreen(), (float) color.getBlue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updatePreferences(Configuration configuration) {
|
||||||
|
Color nodeLabelColor = convertJfxColor(configuration.getNodeLabelColor());
|
||||||
|
Color nodeLabelBackgroundColor = convertJfxColor(configuration.getNodeLabelBackgroundColor());
|
||||||
|
nodeLabelRenderer.setColors(nodeLabelColor, nodeLabelBackgroundColor);
|
||||||
|
throughputLimit = configuration.getThroughputLimit();
|
||||||
|
if (configuration.getThroughputWidth() != null) {
|
||||||
|
throughputWidth = configuration.getThroughputWidth();
|
||||||
|
}
|
||||||
|
graphViewer.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBackground(String imagePath) {
|
||||||
|
try {
|
||||||
|
backgroundPaintable = new BackgroundPaintable<>(imagePath, graphViewer);
|
||||||
|
graphViewer.addPreRenderPaintable(backgroundPaintable);
|
||||||
|
graphViewer.repaint();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.error("error setting background", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeBackground() {
|
||||||
|
if (backgroundPaintable != null) {
|
||||||
|
graphViewer.removePreRenderPaintable(backgroundPaintable);
|
||||||
|
graphViewer.repaint();
|
||||||
|
backgroundPaintable = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMode(ModalGraphMouse.Mode mode) {
|
||||||
|
graphMouse.setMode(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
logger.info("network graph reset");
|
||||||
|
vertexId = 1;
|
||||||
|
linkId = 1;
|
||||||
|
for (CoreNode node : nodeMap.values()) {
|
||||||
|
graph.removeVertex(node);
|
||||||
|
}
|
||||||
|
nodeMap.clear();
|
||||||
|
graphViewer.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updatePositions() {
|
||||||
|
for (CoreNode node : graph.getVertices()) {
|
||||||
|
Double x = graphLayout.getX(node);
|
||||||
|
Double y = graphLayout.getY(node);
|
||||||
|
node.getPosition().setX(x);
|
||||||
|
node.getPosition().setY(y);
|
||||||
|
logger.debug("updating node position node({}): {},{}", node, x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public CoreNode getVertex(int id) {
|
||||||
|
return nodeMap.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private GraphEventListener<CoreNode, CoreLink> graphEventListener = graphEvent -> {
|
||||||
|
logger.info("graph event: {}", graphEvent.getType());
|
||||||
|
switch (graphEvent.getType()) {
|
||||||
|
case EDGE_ADDED:
|
||||||
|
handleEdgeAdded((GraphEvent.Edge<CoreNode, CoreLink>) graphEvent);
|
||||||
|
break;
|
||||||
|
case EDGE_REMOVED:
|
||||||
|
handleEdgeRemoved((GraphEvent.Edge<CoreNode, CoreLink>) graphEvent);
|
||||||
|
break;
|
||||||
|
case VERTEX_ADDED:
|
||||||
|
handleVertexAdded((GraphEvent.Vertex<CoreNode, CoreLink>) graphEvent);
|
||||||
|
break;
|
||||||
|
case VERTEX_REMOVED:
|
||||||
|
handleVertexRemoved((GraphEvent.Vertex<CoreNode, CoreLink>) graphEvent);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private void handleEdgeAdded(GraphEvent.Edge<CoreNode, CoreLink> edgeEvent) {
|
||||||
|
CoreLink link = edgeEvent.getEdge();
|
||||||
|
if (!link.isLoaded()) {
|
||||||
|
Pair<CoreNode> endpoints = graph.getEndpoints(link);
|
||||||
|
|
||||||
|
CoreNode nodeOne = endpoints.getFirst();
|
||||||
|
CoreNode nodeTwo = endpoints.getSecond();
|
||||||
|
|
||||||
|
// create interfaces for nodes
|
||||||
|
int sub = coreAddresses.getSubnet(
|
||||||
|
getInterfaces(nodeOne),
|
||||||
|
getInterfaces(nodeTwo)
|
||||||
|
);
|
||||||
|
|
||||||
|
link.setNodeOne(nodeOne.getId());
|
||||||
|
if (isNode(nodeOne)) {
|
||||||
|
int interfaceOneId = nextInterfaceId(nodeOne);
|
||||||
|
CoreInterface interfaceOne = createInterface(nodeOne, sub, interfaceOneId);
|
||||||
|
link.setInterfaceOne(interfaceOne);
|
||||||
|
}
|
||||||
|
|
||||||
|
link.setNodeTwo(nodeTwo.getId());
|
||||||
|
if (isNode(nodeTwo)) {
|
||||||
|
int interfaceTwoId = nextInterfaceId(nodeTwo);
|
||||||
|
CoreInterface interfaceTwo = createInterface(nodeTwo, sub, interfaceTwoId);
|
||||||
|
link.setInterfaceTwo(interfaceTwo);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isVisible = !checkForWirelessNode(nodeOne, nodeTwo);
|
||||||
|
link.setVisible(isVisible);
|
||||||
|
|
||||||
|
logger.info("adding user created edge: {}", link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<CoreInterface> getInterfaces(CoreNode node) {
|
||||||
|
return graph.getIncidentEdges(node).stream()
|
||||||
|
.map(link -> {
|
||||||
|
if (node.getId().equals(link.getNodeOne())) {
|
||||||
|
return link.getInterfaceOne();
|
||||||
|
} else {
|
||||||
|
return link.getInterfaceTwo();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private int nextInterfaceId(CoreNode node) {
|
||||||
|
Set<Integer> interfaceIds = graph.getIncidentEdges(node).stream()
|
||||||
|
.map(link -> {
|
||||||
|
if (node.getId().equals(link.getNodeOne())) {
|
||||||
|
return link.getInterfaceOne();
|
||||||
|
} else {
|
||||||
|
return link.getInterfaceTwo();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.map(CoreInterface::getId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
while (true) {
|
||||||
|
if (!interfaceIds.contains(i)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isNode(CoreNode node) {
|
||||||
|
return node.getType() == NodeType.DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CoreInterface createInterface(CoreNode node, int sub, int interfaceId) {
|
||||||
|
CoreInterface coreInterface = new CoreInterface();
|
||||||
|
coreInterface.setId(interfaceId);
|
||||||
|
coreInterface.setName(String.format("eth%s", interfaceId));
|
||||||
|
String nodeOneIp4 = coreAddresses.getIp4Address(sub, node.getId());
|
||||||
|
coreInterface.setIp4(nodeOneIp4);
|
||||||
|
coreInterface.setIp4Mask(CoreAddresses.IP4_MASK);
|
||||||
|
return coreInterface;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleEdgeRemoved(GraphEvent.Edge<CoreNode, CoreLink> edgeEvent) {
|
||||||
|
CoreLink link = edgeEvent.getEdge();
|
||||||
|
logger.info("removed edge: {}", link);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleVertexAdded(GraphEvent.Vertex<CoreNode, CoreLink> vertexEvent) {
|
||||||
|
CoreNode node = vertexEvent.getVertex();
|
||||||
|
if (!node.isLoaded()) {
|
||||||
|
node.setNodeType(nodeType);
|
||||||
|
if (node.getType() == NodeType.EMANE) {
|
||||||
|
String emaneModel = controller.getNodeEmaneDialog().getModels().get(0);
|
||||||
|
node.setEmane(emaneModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("adding user created node: {}", node);
|
||||||
|
nodeMap.put(node.getId(), node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleVertexRemoved(GraphEvent.Vertex<CoreNode, CoreLink> vertexEvent) {
|
||||||
|
CoreNode node = vertexEvent.getVertex();
|
||||||
|
logger.info("removed vertex: {}", node);
|
||||||
|
nodeMap.remove(node.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addNode(CoreNode node) {
|
||||||
|
vertexId = Math.max(node.getId() + 1, node.getId());
|
||||||
|
double x = Math.abs(node.getPosition().getX());
|
||||||
|
double y = Math.abs(node.getPosition().getY());
|
||||||
|
logger.info("adding session node: {}", node);
|
||||||
|
graph.addVertex(node);
|
||||||
|
graphLayout.setLocation(node, x, y);
|
||||||
|
nodeMap.put(node.getId(), node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNodeLocation(CoreNode nodeData) {
|
||||||
|
// update actual graph node
|
||||||
|
CoreNode node = nodeMap.get(nodeData.getId());
|
||||||
|
node.getPosition().setX(nodeData.getPosition().getX());
|
||||||
|
node.getPosition().setY(nodeData.getPosition().getY());
|
||||||
|
|
||||||
|
// set graph node location
|
||||||
|
double x = Math.abs(node.getPosition().getX());
|
||||||
|
double y = Math.abs(node.getPosition().getY());
|
||||||
|
graphLayout.setLocation(node, x, y);
|
||||||
|
graphViewer.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeNode(CoreNode node) {
|
||||||
|
try {
|
||||||
|
controller.getCoreClient().deleteNode(node);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.error("error deleting node");
|
||||||
|
Toast.error(String.format("Error deleting node: %s", node.getName()));
|
||||||
|
}
|
||||||
|
graphViewer.getPickedVertexState().pick(node, false);
|
||||||
|
graph.removeVertex(node);
|
||||||
|
graphViewer.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isWirelessNode(CoreNode node) {
|
||||||
|
return node.getType() == NodeType.EMANE || node.getType() == NodeType.WLAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkForWirelessNode(CoreNode nodeOne, CoreNode nodeTwo) {
|
||||||
|
boolean result = isWirelessNode(nodeOne);
|
||||||
|
return result || isWirelessNode(nodeTwo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long wirelessLinkCount(CoreNode node) {
|
||||||
|
return graph.getNeighbors(node).stream()
|
||||||
|
.filter(this::isWirelessNode)
|
||||||
|
.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addLink(CoreLink link) {
|
||||||
|
logger.info("adding session link: {}", link);
|
||||||
|
link.setId(linkId++);
|
||||||
|
CoreNode nodeOne = nodeMap.get(link.getNodeOne());
|
||||||
|
CoreNode nodeTwo = nodeMap.get(link.getNodeTwo());
|
||||||
|
|
||||||
|
boolean isVisible = !checkForWirelessNode(nodeOne, nodeTwo);
|
||||||
|
link.setVisible(isVisible);
|
||||||
|
|
||||||
|
graph.addEdge(link, nodeOne, nodeTwo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeWirelessLink(CoreLink link) {
|
||||||
|
logger.info("deleting link: {}", link);
|
||||||
|
CoreNode nodeOne = nodeMap.get(link.getNodeOne());
|
||||||
|
CoreNode nodeTwo = nodeMap.get(link.getNodeTwo());
|
||||||
|
|
||||||
|
CoreLink existingLink = graph.findEdge(nodeOne, nodeTwo);
|
||||||
|
if (existingLink != null) {
|
||||||
|
graph.removeEdge(existingLink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeLink(CoreLink link) {
|
||||||
|
graphViewer.getPickedEdgeState().pick(link, false);
|
||||||
|
graph.removeEdge(link);
|
||||||
|
graphViewer.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void linkMdrs(CoreNode node) {
|
||||||
|
for (CoreNode currentNode : graph.getVertices()) {
|
||||||
|
if (!"mdr".equals(currentNode.getModel())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// only links mdrs we have not already linked
|
||||||
|
Collection<CoreLink> links = graph.findEdgeSet(node, currentNode);
|
||||||
|
if (links.isEmpty()) {
|
||||||
|
CoreLink link = linkFactory.get();
|
||||||
|
graph.addEdge(link, currentNode, node);
|
||||||
|
graphViewer.repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
116
corefx/src/main/java/com/core/graph/NodeContextMenu.java
Normal file
116
corefx/src/main/java/com/core/graph/NodeContextMenu.java
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
package com.core.graph;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.data.CoreNode;
|
||||||
|
import com.core.ui.Toast;
|
||||||
|
import javafx.scene.control.Menu;
|
||||||
|
import javafx.scene.control.MenuItem;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
class NodeContextMenu extends AbstractNodeContextMenu {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
|
||||||
|
NodeContextMenu(Controller controller, CoreNode coreNode) {
|
||||||
|
super(controller, coreNode);
|
||||||
|
setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
private MenuItem createStartItem(String service) {
|
||||||
|
MenuItem menuItem = new MenuItem("Start");
|
||||||
|
menuItem.setOnAction(event -> {
|
||||||
|
try {
|
||||||
|
boolean result = controller.getCoreClient().startService(coreNode, service);
|
||||||
|
if (result) {
|
||||||
|
Toast.success("Started " + service);
|
||||||
|
} else {
|
||||||
|
Toast.error("Failure to start " + service);
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Toast.error("Error starting " + service, ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return menuItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MenuItem createStopItem(String service) {
|
||||||
|
MenuItem menuItem = new MenuItem("Stop");
|
||||||
|
menuItem.setOnAction(event -> {
|
||||||
|
try {
|
||||||
|
boolean result = controller.getCoreClient().stopService(coreNode, service);
|
||||||
|
if (result) {
|
||||||
|
Toast.success("Stopped " + service);
|
||||||
|
} else {
|
||||||
|
Toast.error("Failure to stop " + service);
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Toast.error("Error stopping " + service, ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return menuItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MenuItem createRestartItem(String service) {
|
||||||
|
MenuItem menuItem = new MenuItem("Restart");
|
||||||
|
menuItem.setOnAction(event -> {
|
||||||
|
try {
|
||||||
|
boolean result = controller.getCoreClient().restartService(coreNode, service);
|
||||||
|
if (result) {
|
||||||
|
Toast.success("Restarted " + service);
|
||||||
|
} else {
|
||||||
|
Toast.error("Failure to restart " + service);
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Toast.error("Error restarting " + service, ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return menuItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MenuItem createValidateItem(String service) {
|
||||||
|
MenuItem menuItem = new MenuItem("Validate");
|
||||||
|
menuItem.setOnAction(event -> {
|
||||||
|
try {
|
||||||
|
boolean result = controller.getCoreClient().validateService(coreNode, service);
|
||||||
|
if (result) {
|
||||||
|
Toast.success("Validated " + service);
|
||||||
|
} else {
|
||||||
|
Toast.error("Validation failed for " + service);
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Toast.error("Error validating " + service, ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return menuItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setup() {
|
||||||
|
if (controller.getCoreClient().isRunning()) {
|
||||||
|
Set<String> services = coreNode.getServices();
|
||||||
|
if (services.isEmpty()) {
|
||||||
|
services = controller.getDefaultServices().getOrDefault(coreNode.getModel(), Collections.emptySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!services.isEmpty()) {
|
||||||
|
Menu menu = new Menu("Manage Services");
|
||||||
|
for (String service : services) {
|
||||||
|
Menu serviceMenu = new Menu(service);
|
||||||
|
MenuItem startItem = createStartItem(service);
|
||||||
|
MenuItem stopItem = createStopItem(service);
|
||||||
|
MenuItem restartItem = createRestartItem(service);
|
||||||
|
MenuItem validateItem = createValidateItem(service);
|
||||||
|
serviceMenu.getItems().addAll(startItem, stopItem, restartItem, validateItem);
|
||||||
|
menu.getItems().add(serviceMenu);
|
||||||
|
}
|
||||||
|
getItems().add(menu);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addMenuItem("Services", event -> controller.getNodeServicesDialog().showDialog(coreNode));
|
||||||
|
addMenuItem("Delete Node", event -> controller.deleteNode(coreNode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
corefx/src/main/java/com/core/graph/RadioIcon.java
Normal file
31
corefx/src/main/java/com/core/graph/RadioIcon.java
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package com.core.graph;
|
||||||
|
|
||||||
|
import com.core.utils.IconUtils;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class RadioIcon implements Icon {
|
||||||
|
private long wiressLinks = 0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getIconHeight() {
|
||||||
|
return IconUtils.ICON_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getIconWidth() {
|
||||||
|
return IconUtils.ICON_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paintIcon(Component c, Graphics g, int x, int y) {
|
||||||
|
g.setColor(Color.black);
|
||||||
|
for (int i = 0; i < wiressLinks; i++) {
|
||||||
|
g.fillOval(x, y, 10, 10);
|
||||||
|
x += 15;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.core.graph;
|
||||||
|
|
||||||
|
import edu.uci.ics.jung.graph.UndirectedSparseGraph;
|
||||||
|
import edu.uci.ics.jung.graph.util.EdgeType;
|
||||||
|
import edu.uci.ics.jung.graph.util.Pair;
|
||||||
|
|
||||||
|
public class UndirectedSimpleGraph<V, E> extends UndirectedSparseGraph<V, E> {
|
||||||
|
@Override
|
||||||
|
public boolean addEdge(E edge, Pair<? extends V> endpoints, EdgeType edgeType) {
|
||||||
|
Pair<V> newEndpoints = getValidatedEndpoints(edge, endpoints);
|
||||||
|
if (newEndpoints == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
V first = newEndpoints.getFirst();
|
||||||
|
V second = newEndpoints.getSecond();
|
||||||
|
|
||||||
|
if (first.equals(second)) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return super.addEdge(edge, endpoints, edgeType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
corefx/src/main/java/com/core/graph/WlanContextMenu.java
Normal file
26
corefx/src/main/java/com/core/graph/WlanContextMenu.java
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package com.core.graph;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.data.CoreNode;
|
||||||
|
import com.core.ui.dialogs.MobilityPlayerDialog;
|
||||||
|
|
||||||
|
class WlanContextMenu extends AbstractNodeContextMenu {
|
||||||
|
WlanContextMenu(Controller controller, CoreNode coreNode) {
|
||||||
|
super(controller, coreNode);
|
||||||
|
setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setup() {
|
||||||
|
addMenuItem("WLAN Settings", event -> controller.getNodeWlanDialog().showDialog(coreNode));
|
||||||
|
if (controller.getCoreClient().isRunning()) {
|
||||||
|
MobilityPlayerDialog mobilityPlayerDialog = controller.getMobilityPlayerDialogs().get(coreNode.getId());
|
||||||
|
if (mobilityPlayerDialog != null && !mobilityPlayerDialog.getStage().isShowing()) {
|
||||||
|
addMenuItem("Mobility Script", event -> mobilityPlayerDialog.show());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addMenuItem("Mobility", event -> controller.getMobilityDialog().showDialog(coreNode));
|
||||||
|
addMenuItem("Link MDRs", event -> controller.getNetworkGraph().linkMdrs(coreNode));
|
||||||
|
addMenuItem("Delete Node", event -> controller.deleteNode(coreNode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
104
corefx/src/main/java/com/core/ui/AnnotationToolbar.java
Normal file
104
corefx/src/main/java/com/core/ui/AnnotationToolbar.java
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
package com.core.ui;
|
||||||
|
|
||||||
|
import com.core.graph.NetworkGraph;
|
||||||
|
import com.core.utils.FxmlUtils;
|
||||||
|
import com.jfoenix.controls.JFXColorPicker;
|
||||||
|
import com.jfoenix.controls.JFXComboBox;
|
||||||
|
import com.jfoenix.controls.JFXToggleButton;
|
||||||
|
import edu.uci.ics.jung.visualization.annotations.Annotation;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.layout.GridPane;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.geom.Ellipse2D;
|
||||||
|
import java.awt.geom.RectangularShape;
|
||||||
|
import java.awt.geom.RoundRectangle2D;
|
||||||
|
|
||||||
|
public class AnnotationToolbar extends GridPane {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private static final String RECTANGLE = "Rectangle";
|
||||||
|
private static final String ROUND_RECTANGLE = "RoundRectangle";
|
||||||
|
private static final String ELLIPSE = "Ellipse";
|
||||||
|
private static final String UPPER_LAYER = "Upper";
|
||||||
|
private static final String LOWER_LAYER = "Lower";
|
||||||
|
private NetworkGraph graph;
|
||||||
|
@FXML private JFXComboBox<String> shapeCombo;
|
||||||
|
@FXML private JFXColorPicker colorPicker;
|
||||||
|
@FXML private JFXComboBox<String> layerCombo;
|
||||||
|
@FXML private JFXToggleButton fillToggle;
|
||||||
|
|
||||||
|
public AnnotationToolbar(NetworkGraph graph) {
|
||||||
|
this.graph = graph;
|
||||||
|
FxmlUtils.loadRootController(this, "/fxml/annotation_toolbar.fxml");
|
||||||
|
|
||||||
|
// setup annotation shape combo
|
||||||
|
shapeCombo.getItems().addAll(RECTANGLE, ROUND_RECTANGLE, ELLIPSE);
|
||||||
|
shapeCombo.setOnAction(this::shapeChange);
|
||||||
|
shapeCombo.getSelectionModel().selectFirst();
|
||||||
|
|
||||||
|
// setup annotation layer combo
|
||||||
|
layerCombo.getItems().addAll(LOWER_LAYER, UPPER_LAYER);
|
||||||
|
layerCombo.setOnAction(this::layerChange);
|
||||||
|
layerCombo.getSelectionModel().selectFirst();
|
||||||
|
|
||||||
|
// setup annotation color picker
|
||||||
|
colorPicker.setOnAction(this::colorChange);
|
||||||
|
colorPicker.setValue(javafx.scene.paint.Color.AQUA);
|
||||||
|
colorPicker.fireEvent(new ActionEvent());
|
||||||
|
|
||||||
|
// setup annotation toggle fill
|
||||||
|
fillToggle.setOnAction(this::fillChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillChange(ActionEvent event) {
|
||||||
|
boolean selected = fillToggle.isSelected();
|
||||||
|
graph.getGraphMouse().getAnnotatingPlugin().setFill(selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void colorChange(ActionEvent event) {
|
||||||
|
javafx.scene.paint.Color fxColor = colorPicker.getValue();
|
||||||
|
java.awt.Color color = new java.awt.Color(
|
||||||
|
(float) fxColor.getRed(),
|
||||||
|
(float) fxColor.getGreen(),
|
||||||
|
(float) fxColor.getBlue(),
|
||||||
|
(float) fxColor.getOpacity()
|
||||||
|
);
|
||||||
|
logger.info("color selected: {}", fxColor);
|
||||||
|
graph.getGraphMouse().getAnnotatingPlugin().setAnnotationColor(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void layerChange(ActionEvent event) {
|
||||||
|
String selected = layerCombo.getSelectionModel().getSelectedItem();
|
||||||
|
logger.info("annotation layer selected: {}", selected);
|
||||||
|
Annotation.Layer layer;
|
||||||
|
if (LOWER_LAYER.equals(selected)) {
|
||||||
|
layer = Annotation.Layer.LOWER;
|
||||||
|
} else {
|
||||||
|
layer = Annotation.Layer.UPPER;
|
||||||
|
}
|
||||||
|
graph.getGraphMouse().getAnnotatingPlugin().setLayer(layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shapeChange(ActionEvent event) {
|
||||||
|
String selected = shapeCombo.getSelectionModel().getSelectedItem();
|
||||||
|
logger.info("annotation shape selected: {}", selected);
|
||||||
|
RectangularShape shape = new Rectangle();
|
||||||
|
switch (selected) {
|
||||||
|
case RECTANGLE:
|
||||||
|
shape = new Rectangle();
|
||||||
|
break;
|
||||||
|
case ROUND_RECTANGLE:
|
||||||
|
shape = new RoundRectangle2D.Double(0, 0, 0, 0, 50.0, 50.0);
|
||||||
|
break;
|
||||||
|
case ELLIPSE:
|
||||||
|
shape = new Ellipse2D.Double();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Toast.error("Unknown annotation shape " + selected);
|
||||||
|
}
|
||||||
|
graph.getGraphMouse().getAnnotatingPlugin().setRectangularShape(shape);
|
||||||
|
}
|
||||||
|
}
|
297
corefx/src/main/java/com/core/ui/GraphToolbar.java
Normal file
297
corefx/src/main/java/com/core/ui/GraphToolbar.java
Normal file
|
@ -0,0 +1,297 @@
|
||||||
|
package com.core.ui;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.data.NodeType;
|
||||||
|
import com.core.utils.FxmlUtils;
|
||||||
|
import com.core.utils.IconUtils;
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import com.jfoenix.controls.JFXListView;
|
||||||
|
import com.jfoenix.controls.JFXPopup;
|
||||||
|
import com.jfoenix.svg.SVGGlyph;
|
||||||
|
import edu.uci.ics.jung.visualization.control.ModalGraphMouse;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.css.PseudoClass;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.ComboBox;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.Tooltip;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class GraphToolbar extends VBox {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private static final int ICON_SIZE = 40;
|
||||||
|
private static final int NODES_ICON_SIZE = 20;
|
||||||
|
private static final PseudoClass START_CLASS = PseudoClass.getPseudoClass("start");
|
||||||
|
private static final PseudoClass STOP_CLASS = PseudoClass.getPseudoClass("stop");
|
||||||
|
private static final PseudoClass SELECTED_CLASS = PseudoClass.getPseudoClass("selected");
|
||||||
|
private final Controller controller;
|
||||||
|
private final Map<Integer, Label> labelMap = new HashMap<>();
|
||||||
|
private SVGGlyph startIcon;
|
||||||
|
private SVGGlyph stopIcon;
|
||||||
|
private JFXListView<Label> nodesList = new JFXListView<>();
|
||||||
|
private JFXListView<Label> devicesList = new JFXListView<>();
|
||||||
|
private JFXButton selectedEditButton;
|
||||||
|
private NodeType selectedNodeType;
|
||||||
|
private boolean isEditing = false;
|
||||||
|
@FXML private JFXButton runButton;
|
||||||
|
@FXML private JFXButton pickingButton;
|
||||||
|
@FXML private JFXButton drawingButton;
|
||||||
|
@FXML private ComboBox<String> graphModeCombo;
|
||||||
|
@FXML private JFXButton nodesButton;
|
||||||
|
@FXML private JFXButton devicesButton;
|
||||||
|
|
||||||
|
public GraphToolbar(Controller controller) {
|
||||||
|
this.controller = controller;
|
||||||
|
FxmlUtils.loadRootController(this, "/fxml/graph_toolbar.fxml");
|
||||||
|
|
||||||
|
startIcon = IconUtils.get("play_circle_filled");
|
||||||
|
startIcon.setSize(ICON_SIZE);
|
||||||
|
stopIcon = IconUtils.get("stop");
|
||||||
|
stopIcon.setSize(ICON_SIZE);
|
||||||
|
|
||||||
|
setupPickingButton();
|
||||||
|
setupDrawingButton();
|
||||||
|
setupNodesButton();
|
||||||
|
setupDevicesButton();
|
||||||
|
|
||||||
|
// initial state
|
||||||
|
setSelected(true, pickingButton);
|
||||||
|
controller.getNetworkGraph().setMode(ModalGraphMouse.Mode.PICKING);
|
||||||
|
runButton.setGraphic(startIcon);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupPickingButton() {
|
||||||
|
SVGGlyph pickingIcon = IconUtils.get("call_made");
|
||||||
|
pickingIcon.setSize(ICON_SIZE);
|
||||||
|
pickingButton.setGraphic(pickingIcon);
|
||||||
|
pickingButton.setTooltip(new Tooltip("Pick/Move Nodes"));
|
||||||
|
pickingButton.setOnAction(event -> {
|
||||||
|
controller.getNetworkGraph().setMode(ModalGraphMouse.Mode.PICKING);
|
||||||
|
controller.getBottom().getChildren().remove(controller.getAnnotationToolbar());
|
||||||
|
controller.getBorderPane().setRight(null);
|
||||||
|
setSelected(true, pickingButton);
|
||||||
|
setSelected(false, drawingButton, selectedEditButton);
|
||||||
|
isEditing = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setEditMode() {
|
||||||
|
controller.getNetworkGraph().setMode(ModalGraphMouse.Mode.EDITING);
|
||||||
|
controller.getBottom().getChildren().remove(controller.getAnnotationToolbar());
|
||||||
|
controller.getBorderPane().setRight(null);
|
||||||
|
if (selectedEditButton != null) {
|
||||||
|
setSelected(true, selectedEditButton);
|
||||||
|
}
|
||||||
|
setSelected(false, drawingButton, pickingButton);
|
||||||
|
isEditing = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupDrawingButton() {
|
||||||
|
SVGGlyph pencilIcon = IconUtils.get("brush");
|
||||||
|
pencilIcon.setSize(ICON_SIZE);
|
||||||
|
drawingButton.setGraphic(pencilIcon);
|
||||||
|
drawingButton.setTooltip(new Tooltip("Annotate Graph"));
|
||||||
|
drawingButton.setOnAction(event -> {
|
||||||
|
controller.getNetworkGraph().setMode(ModalGraphMouse.Mode.ANNOTATING);
|
||||||
|
controller.getBottom().getChildren().add(controller.getAnnotationToolbar());
|
||||||
|
controller.getBorderPane().setRight(null);
|
||||||
|
setSelected(true, drawingButton);
|
||||||
|
setSelected(false, pickingButton, selectedEditButton);
|
||||||
|
isEditing = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setupNodeTypes() {
|
||||||
|
// clear existing configuration
|
||||||
|
labelMap.clear();
|
||||||
|
nodesList.getItems().clear();
|
||||||
|
devicesList.getItems().clear();
|
||||||
|
|
||||||
|
for (NodeType nodeType : NodeType.getAll()) {
|
||||||
|
ImageView icon = new ImageView(nodeType.getIcon());
|
||||||
|
icon.setFitWidth(NODES_ICON_SIZE);
|
||||||
|
icon.setFitHeight(NODES_ICON_SIZE);
|
||||||
|
Label label = new Label(nodeType.getDisplay(), icon);
|
||||||
|
label.setUserData(nodeType.getId());
|
||||||
|
labelMap.put(nodeType.getId(), label);
|
||||||
|
|
||||||
|
if (nodeType.getValue() == NodeType.DEFAULT) {
|
||||||
|
nodesList.getItems().add(label);
|
||||||
|
} else {
|
||||||
|
devicesList.getItems().add(label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Comparator<Label> comparator = Comparator.comparing(Label::getText);
|
||||||
|
nodesList.getItems().sort(comparator);
|
||||||
|
devicesList.getItems().sort(comparator);
|
||||||
|
|
||||||
|
// initial node
|
||||||
|
nodesList.getSelectionModel().selectFirst();
|
||||||
|
Label selectedNodeLabel = nodesList.getSelectionModel().getSelectedItem();
|
||||||
|
selectedNodeType = NodeType.get((int) selectedNodeLabel.getUserData());
|
||||||
|
selectedEditButton = nodesButton;
|
||||||
|
controller.getNetworkGraph().setNodeType(selectedNodeType);
|
||||||
|
updateButtonValues(nodesButton, selectedNodeLabel);
|
||||||
|
|
||||||
|
// initial device
|
||||||
|
updateButtonValues(devicesButton, devicesList.getItems().get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateButtonValues(JFXButton button, Label label) {
|
||||||
|
ImageView icon = new ImageView(((ImageView) label.getGraphic()).getImage());
|
||||||
|
icon.setFitHeight(ICON_SIZE);
|
||||||
|
icon.setFitWidth(ICON_SIZE);
|
||||||
|
button.setGraphic(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setSelectedEditButton(JFXButton button) {
|
||||||
|
JFXButton previous = selectedEditButton;
|
||||||
|
selectedEditButton = button;
|
||||||
|
if (isEditing) {
|
||||||
|
if (previous != null) {
|
||||||
|
setSelected(false, previous);
|
||||||
|
}
|
||||||
|
setSelected(true, selectedEditButton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupNodesButton() {
|
||||||
|
nodesButton.setTooltip(new Tooltip("Network Nodes (host, pc, etc)"));
|
||||||
|
nodesList.setOnMouseClicked(event -> {
|
||||||
|
Label selectedLabel = nodesList.getSelectionModel().getSelectedItem();
|
||||||
|
if (selectedLabel == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateButtonValues(nodesButton, selectedLabel);
|
||||||
|
selectedNodeType = NodeType.get((int) selectedLabel.getUserData());
|
||||||
|
logger.info("selected node type: {}", selectedNodeType);
|
||||||
|
setSelectedEditButton(nodesButton);
|
||||||
|
devicesList.getSelectionModel().clearSelection();
|
||||||
|
controller.getNetworkGraph().setNodeType(selectedNodeType);
|
||||||
|
logger.info("node selected: {} - type: {}", selectedLabel, selectedNodeType);
|
||||||
|
setEditMode();
|
||||||
|
});
|
||||||
|
|
||||||
|
JFXPopup popup = new JFXPopup(nodesList);
|
||||||
|
nodesButton.setOnAction(event -> popup.show(nodesButton, JFXPopup.PopupVPosition.TOP,
|
||||||
|
JFXPopup.PopupHPosition.LEFT, nodesButton.getWidth(), 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupDevicesButton() {
|
||||||
|
devicesButton.setTooltip(new Tooltip("Device Nodes (WLAN, EMANE, Switch, etc)"));
|
||||||
|
devicesList.setOnMouseClicked(event -> {
|
||||||
|
Label selectedLabel = devicesList.getSelectionModel().getSelectedItem();
|
||||||
|
if (selectedLabel == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateButtonValues(devicesButton, selectedLabel);
|
||||||
|
selectedNodeType = NodeType.get((int) selectedLabel.getUserData());
|
||||||
|
logger.info("selected node type: {}", selectedNodeType);
|
||||||
|
controller.getNetworkGraph().setNodeType(selectedNodeType);
|
||||||
|
setSelectedEditButton(devicesButton);
|
||||||
|
nodesList.getSelectionModel().clearSelection();
|
||||||
|
logger.info("device selected: {} - type: {}", selectedLabel, selectedNodeType);
|
||||||
|
setEditMode();
|
||||||
|
});
|
||||||
|
|
||||||
|
JFXPopup popup = new JFXPopup(devicesList);
|
||||||
|
devicesButton.setOnAction(event -> popup.show(devicesButton, JFXPopup.PopupVPosition.TOP,
|
||||||
|
JFXPopup.PopupHPosition.LEFT, devicesButton.getWidth(), 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void onRunButtonAction(ActionEvent event) {
|
||||||
|
if (runButton.getGraphic() == startIcon) {
|
||||||
|
startSession();
|
||||||
|
} else {
|
||||||
|
stopSession();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateNodeType(int id, String uri) {
|
||||||
|
Label label = labelMap.get(id);
|
||||||
|
ImageView icon = new ImageView(uri);
|
||||||
|
icon.setFitWidth(NODES_ICON_SIZE);
|
||||||
|
icon.setFitHeight(NODES_ICON_SIZE);
|
||||||
|
label.setGraphic(icon);
|
||||||
|
|
||||||
|
if (selectedNodeType.getId() == id) {
|
||||||
|
updateButtonValues(nodesButton, label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setSelected(boolean isSelected, JFXButton... others) {
|
||||||
|
Arrays.stream(others)
|
||||||
|
.forEach(x -> x.pseudoClassStateChanged(SELECTED_CLASS, isSelected));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startSession() {
|
||||||
|
runButton.setDisable(true);
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
boolean result = controller.startSession();
|
||||||
|
if (result) {
|
||||||
|
Toast.success("Session Started");
|
||||||
|
setRunButton(true);
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Toast.error("Failure Starting Session", ex);
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopSession() {
|
||||||
|
runButton.setDisable(true);
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
boolean result = controller.stopSession();
|
||||||
|
if (result) {
|
||||||
|
Toast.success("Session Stopped");
|
||||||
|
setRunButton(false);
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Toast.error("Failure Stopping Session", ex);
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRunButton(boolean isRunning) {
|
||||||
|
if (isRunning) {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
pickingButton.fire();
|
||||||
|
devicesButton.setDisable(true);
|
||||||
|
nodesButton.setDisable(true);
|
||||||
|
runButton.pseudoClassStateChanged(START_CLASS, false);
|
||||||
|
runButton.pseudoClassStateChanged(STOP_CLASS, true);
|
||||||
|
if (runButton.getGraphic() != stopIcon) {
|
||||||
|
runButton.setGraphic(stopIcon);
|
||||||
|
}
|
||||||
|
runButton.setDisable(false);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
devicesButton.setDisable(false);
|
||||||
|
nodesButton.setDisable(false);
|
||||||
|
runButton.pseudoClassStateChanged(START_CLASS, true);
|
||||||
|
runButton.pseudoClassStateChanged(STOP_CLASS, false);
|
||||||
|
if (runButton.getGraphic() != startIcon) {
|
||||||
|
runButton.setGraphic(startIcon);
|
||||||
|
}
|
||||||
|
runButton.setDisable(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
178
corefx/src/main/java/com/core/ui/LinkDetails.java
Normal file
178
corefx/src/main/java/com/core/ui/LinkDetails.java
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
package com.core.ui;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.client.ICoreClient;
|
||||||
|
import com.core.data.CoreInterface;
|
||||||
|
import com.core.data.CoreLink;
|
||||||
|
import com.core.data.CoreLinkOptions;
|
||||||
|
import com.core.data.CoreNode;
|
||||||
|
import com.core.graph.NetworkGraph;
|
||||||
|
import com.core.ui.textfields.DoubleFilter;
|
||||||
|
import com.core.utils.FxmlUtils;
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import com.jfoenix.controls.JFXTextField;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.event.EventHandler;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.geometry.Orientation;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.ScrollPane;
|
||||||
|
import javafx.scene.control.Separator;
|
||||||
|
import javafx.scene.control.TextFormatter;
|
||||||
|
import javafx.scene.layout.GridPane;
|
||||||
|
import javafx.util.converter.DoubleStringConverter;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class LinkDetails extends ScrollPane {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private static final int START_INDEX = 1;
|
||||||
|
private final Controller controller;
|
||||||
|
private int index = START_INDEX;
|
||||||
|
@FXML private GridPane gridPane;
|
||||||
|
|
||||||
|
public LinkDetails(Controller controller) {
|
||||||
|
this.controller = controller;
|
||||||
|
FxmlUtils.loadRootController(this, "/fxml/link_details.fxml");
|
||||||
|
setPrefWidth(400);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLink(CoreLink link) {
|
||||||
|
NetworkGraph graph = controller.getNetworkGraph();
|
||||||
|
ICoreClient coreClient = controller.getCoreClient();
|
||||||
|
|
||||||
|
clear();
|
||||||
|
|
||||||
|
addSeparator();
|
||||||
|
CoreNode nodeOne = graph.getVertex(link.getNodeOne());
|
||||||
|
CoreInterface interfaceOne = link.getInterfaceOne();
|
||||||
|
addLabel(nodeOne.getName());
|
||||||
|
if (interfaceOne != null) {
|
||||||
|
addInterface(interfaceOne);
|
||||||
|
}
|
||||||
|
|
||||||
|
addSeparator();
|
||||||
|
CoreNode nodeTwo = graph.getVertex(link.getNodeTwo());
|
||||||
|
CoreInterface interfaceTwo = link.getInterfaceTwo();
|
||||||
|
addLabel(nodeTwo.getName());
|
||||||
|
if (interfaceTwo != null) {
|
||||||
|
addInterface(interfaceTwo);
|
||||||
|
}
|
||||||
|
|
||||||
|
addSeparator();
|
||||||
|
addLabel("Properties");
|
||||||
|
JFXTextField bandwidthField = addRow("Bandwidth (bps)", link.getOptions().getBandwidth());
|
||||||
|
JFXTextField delayField = addRow("Delay (us)", link.getOptions().getDelay());
|
||||||
|
JFXTextField jitterField = addRow("Jitter (us)", link.getOptions().getJitter());
|
||||||
|
JFXTextField lossField = addRow("Loss (%)", link.getOptions().getPer());
|
||||||
|
JFXTextField dupsField = addRow("Duplicate (%)", link.getOptions().getDup());
|
||||||
|
addButton("Update", event -> {
|
||||||
|
CoreLinkOptions options = link.getOptions();
|
||||||
|
options.setBandwidth(getDouble(bandwidthField));
|
||||||
|
options.setDelay(getDouble(delayField));
|
||||||
|
options.setJitter(getDouble(jitterField));
|
||||||
|
options.setPer(getDouble(lossField));
|
||||||
|
options.setDup(getDouble(dupsField));
|
||||||
|
|
||||||
|
if (coreClient.isRunning()) {
|
||||||
|
try {
|
||||||
|
coreClient.editLink(link);
|
||||||
|
Toast.info("Link updated!");
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Toast.error("Failure to update link", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Double getDouble(JFXTextField textField) {
|
||||||
|
if (textField.getText() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Double value = null;
|
||||||
|
try {
|
||||||
|
logger.info("double field text: {}", textField.getText());
|
||||||
|
value = Double.parseDouble(textField.getText());
|
||||||
|
} catch (NumberFormatException ex) {
|
||||||
|
logger.error("error getting double value", ex);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addButton(String text, EventHandler<ActionEvent> handler) {
|
||||||
|
JFXButton button = new JFXButton(text);
|
||||||
|
button.getStyleClass().add("core-button");
|
||||||
|
button.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
button.setOnAction(handler);
|
||||||
|
gridPane.add(button, 0, index++, 2, 1);
|
||||||
|
GridPane.setMargin(button, new Insets(10, 0, 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addLabel(String text) {
|
||||||
|
Label label = new Label(text);
|
||||||
|
label.getStyleClass().add("details-label");
|
||||||
|
gridPane.add(label, 0, index++, 2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addSeparator() {
|
||||||
|
Separator separator = new Separator(Orientation.HORIZONTAL);
|
||||||
|
gridPane.add(separator, 0, index++, 2, 1);
|
||||||
|
GridPane.setMargin(separator, new Insets(10, 0, 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addInterface(CoreInterface coreInterface) {
|
||||||
|
addRow("Interface", coreInterface.getName(), true);
|
||||||
|
if (coreInterface.getMac() != null) {
|
||||||
|
addRow("MAC", coreInterface.getMac(), true);
|
||||||
|
}
|
||||||
|
addIp4Address(coreInterface.getIp4(), coreInterface.getIp4Mask());
|
||||||
|
addIp6Address(coreInterface.getIp6(), coreInterface.getIp6Mask());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRow(String labelText, String value, boolean disabled) {
|
||||||
|
Label label = new Label(labelText);
|
||||||
|
JFXTextField textField = new JFXTextField(value);
|
||||||
|
textField.setDisable(disabled);
|
||||||
|
gridPane.addRow(index++, label, textField);
|
||||||
|
}
|
||||||
|
|
||||||
|
private JFXTextField addRow(String labelText, Double value) {
|
||||||
|
Label label = new Label(labelText);
|
||||||
|
String doubleString = null;
|
||||||
|
if (value != null) {
|
||||||
|
doubleString = value.toString();
|
||||||
|
}
|
||||||
|
JFXTextField textField = new JFXTextField();
|
||||||
|
TextFormatter<Double> formatter = new TextFormatter<>(
|
||||||
|
new DoubleStringConverter(), null, new DoubleFilter());
|
||||||
|
textField.setTextFormatter(formatter);
|
||||||
|
textField.setText(doubleString);
|
||||||
|
gridPane.addRow(index++, label, textField);
|
||||||
|
return textField;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addIp4Address(String ip, Integer mask) {
|
||||||
|
if (ip == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
addRow("IP4", String.format("%s/%s", ip, mask), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addIp6Address(String ip, String mask) {
|
||||||
|
if (ip == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
addRow("IP6", String.format("%s/%s", ip, mask), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clear() {
|
||||||
|
if (gridPane.getChildren().size() > START_INDEX) {
|
||||||
|
gridPane.getChildren().remove(START_INDEX, gridPane.getChildren().size());
|
||||||
|
}
|
||||||
|
index = START_INDEX;
|
||||||
|
}
|
||||||
|
}
|
181
corefx/src/main/java/com/core/ui/NodeDetails.java
Normal file
181
corefx/src/main/java/com/core/ui/NodeDetails.java
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
package com.core.ui;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.data.CoreInterface;
|
||||||
|
import com.core.data.CoreLink;
|
||||||
|
import com.core.data.CoreNode;
|
||||||
|
import com.core.data.NodeType;
|
||||||
|
import com.core.utils.FxmlUtils;
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import com.jfoenix.controls.JFXListView;
|
||||||
|
import com.jfoenix.controls.JFXScrollPane;
|
||||||
|
import com.jfoenix.controls.JFXTextField;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.event.EventHandler;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.geometry.Orientation;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.ScrollPane;
|
||||||
|
import javafx.scene.control.Separator;
|
||||||
|
import javafx.scene.layout.GridPane;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class NodeDetails extends ScrollPane {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private static final int START_INDEX = 1;
|
||||||
|
private final Controller controller;
|
||||||
|
@FXML private Label title;
|
||||||
|
@FXML private ScrollPane scrollPane;
|
||||||
|
@FXML private GridPane gridPane;
|
||||||
|
private int index = START_INDEX;
|
||||||
|
|
||||||
|
public NodeDetails(Controller controller) {
|
||||||
|
this.controller = controller;
|
||||||
|
FxmlUtils.loadRootController(this, "/fxml/node_details.fxml");
|
||||||
|
setPrefWidth(400);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNode(CoreNode node) {
|
||||||
|
clear();
|
||||||
|
title.setText(node.getName());
|
||||||
|
|
||||||
|
addSeparator();
|
||||||
|
addLabel("Properties");
|
||||||
|
if (node.getType() == NodeType.DEFAULT) {
|
||||||
|
addRow("Model", node.getModel(), true);
|
||||||
|
} else {
|
||||||
|
addRow("Type", node.getNodeType().getDisplay(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.getEmane() != null) {
|
||||||
|
addRow("EMANE", node.getEmane(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
addSeparator();
|
||||||
|
addLabel("Position");
|
||||||
|
if (node.getPosition().getX() != null) {
|
||||||
|
addRow("X", node.getPosition().getX().toString(), true);
|
||||||
|
}
|
||||||
|
if (node.getPosition().getY() != null) {
|
||||||
|
addRow("Y", node.getPosition().getY().toString(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
addSeparator();
|
||||||
|
addLabel("Interfaces");
|
||||||
|
for (CoreLink link : controller.getNetworkGraph().getGraph().getIncidentEdges(node)) {
|
||||||
|
CoreNode linkedNode;
|
||||||
|
CoreInterface coreInterface;
|
||||||
|
if (node.getId().equals(link.getNodeOne())) {
|
||||||
|
coreInterface = link.getInterfaceOne();
|
||||||
|
linkedNode = controller.getNetworkGraph().getNodeMap().get(link.getNodeTwo());
|
||||||
|
} else {
|
||||||
|
coreInterface = link.getInterfaceTwo();
|
||||||
|
linkedNode = controller.getNetworkGraph().getNodeMap().get(link.getNodeOne());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (coreInterface == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
addSeparator();
|
||||||
|
if (linkedNode.getType() == NodeType.EMANE) {
|
||||||
|
String emaneModel = linkedNode.getEmane();
|
||||||
|
String linkedLabel = String.format("%s - %s", linkedNode.getName(), emaneModel);
|
||||||
|
addButton(linkedLabel, event -> controller.getNodeEmaneDialog()
|
||||||
|
.displayEmaneModelConfig(linkedNode.getId(), emaneModel));
|
||||||
|
String nodeLabel = String.format("%s - %s", node.getName(), emaneModel);
|
||||||
|
addButton(nodeLabel, event -> controller.getNodeEmaneDialog()
|
||||||
|
.displayEmaneModelConfig(node.getId(), emaneModel));
|
||||||
|
String interfaceLabel = String.format("%s - %s", coreInterface.getName(), emaneModel);
|
||||||
|
Integer interfaceId = 1000 * node.getId() + coreInterface.getId();
|
||||||
|
addButton(interfaceLabel, event -> controller.getNodeEmaneDialog()
|
||||||
|
.displayEmaneModelConfig(interfaceId, emaneModel));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (linkedNode.getType() == NodeType.WLAN) {
|
||||||
|
addButton(linkedNode.getName(), event -> controller.getNodeWlanDialog().showDialog(linkedNode));
|
||||||
|
}
|
||||||
|
addInterface(coreInterface, linkedNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// display custom or default node services
|
||||||
|
Set<String> services = node.getServices();
|
||||||
|
if (services.isEmpty()) {
|
||||||
|
services = controller.getDefaultServices().getOrDefault(node.getModel(), Collections.emptySet());
|
||||||
|
}
|
||||||
|
if (!services.isEmpty()) {
|
||||||
|
addSeparator();
|
||||||
|
addLabel("Services");
|
||||||
|
JFXListView<String> listView = new JFXListView<>();
|
||||||
|
listView.setMouseTransparent(true);
|
||||||
|
listView.setFocusTraversable(false);
|
||||||
|
listView.getItems().setAll(services);
|
||||||
|
gridPane.add(listView, 0, index++, 2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
JFXScrollPane.smoothScrolling(scrollPane);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addButton(String text, EventHandler<ActionEvent> handler) {
|
||||||
|
JFXButton emaneButton = new JFXButton(text);
|
||||||
|
emaneButton.getStyleClass().add("core-button");
|
||||||
|
emaneButton.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
emaneButton.setOnAction(handler);
|
||||||
|
gridPane.add(emaneButton, 0, index++, 2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addLabel(String text) {
|
||||||
|
Label label = new Label(text);
|
||||||
|
label.getStyleClass().add("details-label");
|
||||||
|
gridPane.add(label, 0, index++, 2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addSeparator() {
|
||||||
|
Separator separator = new Separator(Orientation.HORIZONTAL);
|
||||||
|
gridPane.add(separator, 0, index++, 2, 1);
|
||||||
|
GridPane.setMargin(separator, new Insets(10, 0, 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addInterface(CoreInterface coreInterface, CoreNode linkedNode) {
|
||||||
|
addRow("Linked To", linkedNode.getName(), true);
|
||||||
|
addRow("Interface", coreInterface.getName(), true);
|
||||||
|
if (coreInterface.getMac() != null) {
|
||||||
|
addRow("MAC", coreInterface.getMac(), true);
|
||||||
|
}
|
||||||
|
addIp4Address(coreInterface.getIp4(), coreInterface.getIp4Mask());
|
||||||
|
addIp6Address(coreInterface.getIp6(), coreInterface.getIp6Mask());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRow(String labelText, String value, boolean disabled) {
|
||||||
|
Label label = new Label(labelText);
|
||||||
|
JFXTextField textField = new JFXTextField(value);
|
||||||
|
textField.setDisable(disabled);
|
||||||
|
gridPane.addRow(index++, label, textField);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addIp4Address(String ip, Integer mask) {
|
||||||
|
if (ip == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
addRow("IP4", String.format("%s/%s", ip, mask), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addIp6Address(String ip, String mask) {
|
||||||
|
if (ip == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
addRow("IP6", String.format("%s/%s", ip, mask), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clear() {
|
||||||
|
if (gridPane.getChildren().size() > START_INDEX) {
|
||||||
|
gridPane.getChildren().remove(START_INDEX, gridPane.getChildren().size());
|
||||||
|
}
|
||||||
|
index = START_INDEX;
|
||||||
|
}
|
||||||
|
}
|
15
corefx/src/main/java/com/core/ui/ServiceItem.java
Normal file
15
corefx/src/main/java/com/core/ui/ServiceItem.java
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package com.core.ui;
|
||||||
|
|
||||||
|
import com.jfoenix.controls.JFXCheckBox;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ServiceItem {
|
||||||
|
private String service;
|
||||||
|
private JFXCheckBox checkBox;
|
||||||
|
|
||||||
|
public ServiceItem(String service) {
|
||||||
|
this.service = service;
|
||||||
|
checkBox = new JFXCheckBox(service);
|
||||||
|
}
|
||||||
|
}
|
52
corefx/src/main/java/com/core/ui/Toast.java
Normal file
52
corefx/src/main/java/com/core/ui/Toast.java
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
package com.core.ui;
|
||||||
|
|
||||||
|
import com.jfoenix.controls.JFXSnackbar;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
public final class Toast {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private static final long TIMEOUT = 3000;
|
||||||
|
private static JFXSnackbar snackbar;
|
||||||
|
|
||||||
|
private Toast() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setSnackbarRoot(StackPane stackPane) {
|
||||||
|
snackbar = new JFXSnackbar(stackPane);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void toast(String message, String className) {
|
||||||
|
JFXSnackbar.SnackbarEvent snackbarEvent = new JFXSnackbar.SnackbarEvent(message,
|
||||||
|
className, null, TIMEOUT, false, null);
|
||||||
|
snackbar.enqueue(snackbarEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void info(String message) {
|
||||||
|
toast(message, "toast-info");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void success(String message) {
|
||||||
|
toast(message, "toast-success");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void warning(String message) {
|
||||||
|
toast(message, "toast-warning");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void error(String message) {
|
||||||
|
error(message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void error(String message, Exception ex) {
|
||||||
|
if (ex != null) {
|
||||||
|
logger.error(message, ex);
|
||||||
|
}
|
||||||
|
JFXSnackbar.SnackbarEvent snackbarEvent = new JFXSnackbar.SnackbarEvent(message,
|
||||||
|
"toast-error", "X", TIMEOUT, true, event -> snackbar.close());
|
||||||
|
snackbar.close();
|
||||||
|
snackbar.enqueue(snackbarEvent);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
package com.core.ui.dialogs;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.data.CoreLink;
|
||||||
|
import com.core.data.CoreNode;
|
||||||
|
import com.core.graph.BackgroundPaintable;
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import com.jfoenix.controls.JFXTextField;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.stage.FileChooser;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
public class BackgroundDialog extends StageDialog {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
@FXML private ImageView imageView;
|
||||||
|
@FXML private JFXTextField fileTextField;
|
||||||
|
@FXML private JFXButton fileButton;
|
||||||
|
private JFXButton saveButton;
|
||||||
|
private JFXButton clearButton;
|
||||||
|
|
||||||
|
public BackgroundDialog(Controller controller) {
|
||||||
|
super(controller, "/fxml/background_dialog.fxml");
|
||||||
|
setTitle("Background Configuration");
|
||||||
|
saveButton = createButton("Save");
|
||||||
|
saveButton.setOnAction(this::saveAction);
|
||||||
|
|
||||||
|
clearButton = createButton("Clear");
|
||||||
|
clearButton.setOnAction(this::clearAction);
|
||||||
|
addCancelButton();
|
||||||
|
|
||||||
|
HBox parent = (HBox) imageView.getParent();
|
||||||
|
imageView.fitHeightProperty().bind(parent.heightProperty());
|
||||||
|
|
||||||
|
fileButton.setOnAction(this::fileAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fileAction(ActionEvent event) {
|
||||||
|
FileChooser fileChooser = new FileChooser();
|
||||||
|
fileChooser.setTitle("Select Background");
|
||||||
|
fileChooser.setInitialDirectory(new File(System.getProperty("user.home")));
|
||||||
|
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("PNG", "*.png"));
|
||||||
|
File file = fileChooser.showOpenDialog(getStage());
|
||||||
|
if (file != null) {
|
||||||
|
String uri = file.toURI().toString();
|
||||||
|
imageView.setImage(new Image(uri));
|
||||||
|
fileTextField.setText(file.getPath());
|
||||||
|
saveButton.setDisable(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveAction(ActionEvent event) {
|
||||||
|
getController().getNetworkGraph().setBackground(fileTextField.getText());
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearAction(ActionEvent event) {
|
||||||
|
getController().getNetworkGraph().removeBackground();
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showDialog() {
|
||||||
|
BackgroundPaintable<CoreNode, CoreLink> backgroundPaintable = getController().getNetworkGraph()
|
||||||
|
.getBackgroundPaintable();
|
||||||
|
saveButton.setDisable(true);
|
||||||
|
fileTextField.setText(null);
|
||||||
|
imageView.setImage(null);
|
||||||
|
if (backgroundPaintable == null) {
|
||||||
|
clearButton.setDisable(true);
|
||||||
|
} else {
|
||||||
|
String imagePath = backgroundPaintable.getImage();
|
||||||
|
fileTextField.setText(imagePath);
|
||||||
|
imageView.setImage(new Image(Paths.get(imagePath).toUri().toString()));
|
||||||
|
clearButton.setDisable(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
}
|
217
corefx/src/main/java/com/core/ui/dialogs/ChartDialog.java
Normal file
217
corefx/src/main/java/com/core/ui/dialogs/ChartDialog.java
Normal file
|
@ -0,0 +1,217 @@
|
||||||
|
package com.core.ui.dialogs;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.datavis.*;
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import com.jfoenix.controls.JFXComboBox;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.chart.Chart;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
public class ChartDialog extends StageDialog {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private final AtomicBoolean running = new AtomicBoolean(false);
|
||||||
|
private final Random numbers = new Random();
|
||||||
|
private final List<String> chartNames = Arrays.asList("Name 1", "Name 2", "Name 3", "Name 4", "Name 5");
|
||||||
|
@FXML private JFXComboBox<String> chartCombo;
|
||||||
|
@FXML private Pane chartPane;
|
||||||
|
@FXML private JFXButton stopButton;
|
||||||
|
private CoreGraph coreGraph;
|
||||||
|
|
||||||
|
public ChartDialog(Controller controller) {
|
||||||
|
super(controller, "/fxml/chart_dialog.fxml");
|
||||||
|
addCancelButton();
|
||||||
|
|
||||||
|
coreGraph = new CoreGraph();
|
||||||
|
coreGraph.setTitle("My Graph");
|
||||||
|
coreGraph.setXAxis(new CoreGraphAxis("X Label", 0.0, 100.0, 1.0));
|
||||||
|
coreGraph.setYAxis(new CoreGraphAxis("Y Label", 0.0, 100.0, 1.0));
|
||||||
|
|
||||||
|
chartCombo.getItems().addAll("pie", "line", "area", "bar", "scatter", "bubble", "time");
|
||||||
|
chartCombo.getSelectionModel().selectedItemProperty().addListener((ov, prev, curr) -> {
|
||||||
|
if (curr == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
running.set(false);
|
||||||
|
switch (curr) {
|
||||||
|
case "pie":
|
||||||
|
pieChart();
|
||||||
|
break;
|
||||||
|
case "line":
|
||||||
|
lineChart();
|
||||||
|
break;
|
||||||
|
case "area":
|
||||||
|
areaChart();
|
||||||
|
break;
|
||||||
|
case "bar":
|
||||||
|
barChart();
|
||||||
|
break;
|
||||||
|
case "scatter":
|
||||||
|
scatterChart();
|
||||||
|
break;
|
||||||
|
case "bubble":
|
||||||
|
bubbleChart();
|
||||||
|
break;
|
||||||
|
case "time":
|
||||||
|
timeChart();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
stopButton.setOnAction(event -> running.set(false));
|
||||||
|
chartCombo.getSelectionModel().selectFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void timeChart() {
|
||||||
|
coreGraph.setGraphType(GraphType.TIME);
|
||||||
|
CoreGraphWrapper graphWrapper = new CoreGraphWrapper(coreGraph);
|
||||||
|
setChart(graphWrapper.getChart());
|
||||||
|
|
||||||
|
new Thread(() -> {
|
||||||
|
while (running.get()) {
|
||||||
|
try {
|
||||||
|
double y = numbers.nextInt(100);
|
||||||
|
Platform.runLater(() -> graphWrapper.add(new CoreGraphData(null, null, y, null)));
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
logger.error("error adding data", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bubbleChart() {
|
||||||
|
coreGraph.setGraphType(GraphType.BUBBLE);
|
||||||
|
CoreGraphWrapper graphWrapper = new CoreGraphWrapper(coreGraph);
|
||||||
|
setChart(graphWrapper.getChart());
|
||||||
|
|
||||||
|
new Thread(() -> {
|
||||||
|
while (running.get()) {
|
||||||
|
try {
|
||||||
|
double x = numbers.nextInt(100);
|
||||||
|
double y = numbers.nextInt(100);
|
||||||
|
double weight = numbers.nextInt(10);
|
||||||
|
Platform.runLater(() -> graphWrapper.add(new CoreGraphData(null, x, y, weight)));
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
logger.error("error adding data", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scatterChart() {
|
||||||
|
coreGraph.setGraphType(GraphType.SCATTER);
|
||||||
|
CoreGraphWrapper graphWrapper = new CoreGraphWrapper(coreGraph);
|
||||||
|
setChart(graphWrapper.getChart());
|
||||||
|
|
||||||
|
new Thread(() -> {
|
||||||
|
while (running.get()) {
|
||||||
|
try {
|
||||||
|
double x = numbers.nextInt(100);
|
||||||
|
double y = numbers.nextInt(100);
|
||||||
|
Platform.runLater(() -> graphWrapper.add(new CoreGraphData(null, x, y, null)));
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
logger.error("error adding data", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void areaChart() {
|
||||||
|
coreGraph.setGraphType(GraphType.AREA);
|
||||||
|
CoreGraphWrapper graphWrapper = new CoreGraphWrapper(coreGraph);
|
||||||
|
setChart(graphWrapper.getChart());
|
||||||
|
|
||||||
|
new Thread(() -> {
|
||||||
|
while (running.get()) {
|
||||||
|
try {
|
||||||
|
double x = numbers.nextInt(100);
|
||||||
|
double y = numbers.nextInt(100);
|
||||||
|
Platform.runLater(() -> graphWrapper.add(new CoreGraphData(null, x, y, null)));
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
logger.error("error adding data", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setChart(Chart chart) {
|
||||||
|
chart.prefHeightProperty().bind(chartPane.heightProperty());
|
||||||
|
chart.prefWidthProperty().bind(chartPane.widthProperty());
|
||||||
|
chartPane.getChildren().clear();
|
||||||
|
chartPane.getChildren().add(chart);
|
||||||
|
running.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void lineChart() {
|
||||||
|
coreGraph.setGraphType(GraphType.LINE);
|
||||||
|
CoreGraphWrapper graphWrapper = new CoreGraphWrapper(coreGraph);
|
||||||
|
setChart(graphWrapper.getChart());
|
||||||
|
|
||||||
|
new Thread(() -> {
|
||||||
|
while (running.get()) {
|
||||||
|
try {
|
||||||
|
double x = numbers.nextInt(100);
|
||||||
|
double y = numbers.nextInt(100);
|
||||||
|
Platform.runLater(() -> graphWrapper.add(new CoreGraphData(null, x, y, null)));
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
logger.error("error adding data", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pieChart() {
|
||||||
|
coreGraph.setGraphType(GraphType.PIE);
|
||||||
|
CoreGraphWrapper graphWrapper = new CoreGraphWrapper(coreGraph);
|
||||||
|
setChart(graphWrapper.getChart());
|
||||||
|
new Thread(() -> {
|
||||||
|
while (running.get()) {
|
||||||
|
try {
|
||||||
|
String name = chartNames.get(numbers.nextInt(chartNames.size()));
|
||||||
|
double y = numbers.nextInt(100);
|
||||||
|
Platform.runLater(() -> graphWrapper.add(new CoreGraphData(name, null, y, null)));
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
logger.error("error adding data", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void barChart() {
|
||||||
|
coreGraph.setGraphType(GraphType.BAR);
|
||||||
|
CoreGraphWrapper graphWrapper = new CoreGraphWrapper(coreGraph);
|
||||||
|
setChart(graphWrapper.getChart());
|
||||||
|
new Thread(() -> {
|
||||||
|
while (running.get()) {
|
||||||
|
try {
|
||||||
|
String name = chartNames.get(numbers.nextInt(chartNames.size()));
|
||||||
|
Integer y = numbers.nextInt(100);
|
||||||
|
Platform.runLater(() -> graphWrapper.add(name, y));
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
logger.error("error adding data", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void showDialog() {
|
||||||
|
chartCombo.getSelectionModel().selectFirst();
|
||||||
|
}
|
||||||
|
}
|
91
corefx/src/main/java/com/core/ui/dialogs/ConfigDialog.java
Normal file
91
corefx/src/main/java/com/core/ui/dialogs/ConfigDialog.java
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
package com.core.ui.dialogs;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.data.ConfigGroup;
|
||||||
|
import com.core.data.ConfigOption;
|
||||||
|
import com.core.ui.config.ConfigItemUtils;
|
||||||
|
import com.core.ui.config.IConfigItem;
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import com.jfoenix.controls.JFXScrollPane;
|
||||||
|
import com.jfoenix.controls.JFXTabPane;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.control.ScrollPane;
|
||||||
|
import javafx.scene.control.Tab;
|
||||||
|
import javafx.scene.layout.ColumnConstraints;
|
||||||
|
import javafx.scene.layout.GridPane;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class ConfigDialog extends StageDialog {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private List<IConfigItem> configItems = new ArrayList<>();
|
||||||
|
private JFXButton saveButton;
|
||||||
|
@FXML private JFXTabPane tabPane;
|
||||||
|
|
||||||
|
public ConfigDialog(Controller controller) {
|
||||||
|
super(controller, "/fxml/config_dialog.fxml");
|
||||||
|
saveButton = createButton("Save");
|
||||||
|
addCancelButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ConfigOption> getOptions() {
|
||||||
|
return configItems.stream().map(IConfigItem::getOption).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setDisabled(boolean isDisabled) {
|
||||||
|
saveButton.setDisable(isDisabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showDialog(String title, List<ConfigGroup> configGroups, Runnable runnable) {
|
||||||
|
setTitle(title);
|
||||||
|
boolean sessionRunning = getCoreClient().isRunning();
|
||||||
|
setDisabled(sessionRunning);
|
||||||
|
|
||||||
|
configItems.clear();
|
||||||
|
tabPane.getTabs().clear();
|
||||||
|
for (ConfigGroup group : configGroups) {
|
||||||
|
String groupName = group.getName();
|
||||||
|
Tab tab = new Tab(groupName);
|
||||||
|
ScrollPane scrollPane = new ScrollPane();
|
||||||
|
scrollPane.setFitToWidth(true);
|
||||||
|
tab.setContent(scrollPane);
|
||||||
|
GridPane gridPane = new GridPane();
|
||||||
|
gridPane.setPadding(new Insets(10));
|
||||||
|
scrollPane.setContent(gridPane);
|
||||||
|
gridPane.setPrefWidth(Double.MAX_VALUE);
|
||||||
|
ColumnConstraints labelConstraints = new ColumnConstraints(10);
|
||||||
|
labelConstraints.setPercentWidth(50);
|
||||||
|
ColumnConstraints valueConstraints = new ColumnConstraints(10);
|
||||||
|
valueConstraints.setPercentWidth(50);
|
||||||
|
gridPane.getColumnConstraints().addAll(labelConstraints, valueConstraints);
|
||||||
|
gridPane.setHgap(10);
|
||||||
|
gridPane.setVgap(10);
|
||||||
|
int index = 0;
|
||||||
|
tabPane.getTabs().add(tab);
|
||||||
|
|
||||||
|
for (ConfigOption option : group.getOptions()) {
|
||||||
|
IConfigItem configItem = ConfigItemUtils.get(getStage(), option);
|
||||||
|
Node node = configItem.getNode();
|
||||||
|
node.setDisable(sessionRunning);
|
||||||
|
gridPane.addRow(index, configItem.getLabel(), node);
|
||||||
|
configItems.add(configItem);
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
JFXScrollPane.smoothScrolling(scrollPane);
|
||||||
|
}
|
||||||
|
|
||||||
|
saveButton.setOnAction(event -> {
|
||||||
|
runnable.run();
|
||||||
|
close();
|
||||||
|
});
|
||||||
|
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
}
|
39
corefx/src/main/java/com/core/ui/dialogs/ConnectDialog.java
Normal file
39
corefx/src/main/java/com/core/ui/dialogs/ConnectDialog.java
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
package com.core.ui.dialogs;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import com.jfoenix.controls.JFXTextField;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ConnectDialog extends StageDialog {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private String address;
|
||||||
|
private int port;
|
||||||
|
private JFXButton saveButton;
|
||||||
|
@FXML JFXTextField addressTextField;
|
||||||
|
@FXML JFXTextField portTextField;
|
||||||
|
|
||||||
|
public ConnectDialog(Controller controller) {
|
||||||
|
super(controller, "/fxml/connect_dialog.fxml");
|
||||||
|
saveButton = createButton("Connect");
|
||||||
|
saveButton.setOnAction(event -> {
|
||||||
|
address = addressTextField.getText();
|
||||||
|
port = Integer.parseInt(portTextField.getText());
|
||||||
|
controller.connectToCore(address, port);
|
||||||
|
close();
|
||||||
|
});
|
||||||
|
addCancelButton();
|
||||||
|
setTitle("CORE Connection");
|
||||||
|
getStage().sizeToScene();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showDialog() {
|
||||||
|
addressTextField.setText(address);
|
||||||
|
portTextField.setText(Integer.toString(port));
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
package com.core.ui.dialogs;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.client.ICoreClient;
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import com.jfoenix.controls.JFXDialog;
|
||||||
|
import com.jfoenix.controls.JFXDialogLayout;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.scene.Parent;
|
||||||
|
import javafx.scene.text.Text;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class CoreFoenixDialog extends JFXDialog {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private final Controller controller;
|
||||||
|
private final JFXDialog dialog;
|
||||||
|
private final JFXDialogLayout dialogLayout = new JFXDialogLayout();
|
||||||
|
private final Text heading = new Text();
|
||||||
|
|
||||||
|
public CoreFoenixDialog(Controller controller, String fxmlPath) {
|
||||||
|
this.controller = controller;
|
||||||
|
|
||||||
|
FXMLLoader loader = new FXMLLoader(getClass().getResource(fxmlPath));
|
||||||
|
loader.setController(this);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Parent parent = loader.load();
|
||||||
|
dialogLayout.setBody(parent);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.error("error loading fxml: {}", fxmlPath, ex);
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
dialogLayout.setHeading(heading);
|
||||||
|
dialog = new JFXDialog(controller.getStackPane(), dialogLayout, DialogTransition.CENTER);
|
||||||
|
dialogLayout.setPrefWidth(800);
|
||||||
|
dialogLayout.setPrefHeight(600);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOwner(Stage window) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICoreClient getCoreClient() {
|
||||||
|
return controller.getCoreClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
public JFXButton createButton(String text) {
|
||||||
|
JFXButton button = new JFXButton(text);
|
||||||
|
button.getStyleClass().add("core-button");
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
}
|
28
corefx/src/main/java/com/core/ui/dialogs/GeoDialog.java
Normal file
28
corefx/src/main/java/com/core/ui/dialogs/GeoDialog.java
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package com.core.ui.dialogs;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.web.WebView;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
public class GeoDialog extends StageDialog {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
@FXML private WebView webView;
|
||||||
|
@FXML JFXButton button;
|
||||||
|
|
||||||
|
public GeoDialog(Controller controller) {
|
||||||
|
super(controller, "/fxml/geo_dialog.fxml");
|
||||||
|
setTitle("Geo Display");
|
||||||
|
addCancelButton();
|
||||||
|
webView.getEngine().load(getClass().getResource("/html/geo.html").toExternalForm());
|
||||||
|
button.setOnAction(event -> {
|
||||||
|
webView.getEngine().executeScript("randomMarker();");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showDialog() {
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
package com.core.ui.dialogs;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.ui.Toast;
|
||||||
|
import com.core.utils.ConfigUtils;
|
||||||
|
import com.core.utils.Configuration;
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import com.jfoenix.controls.JFXColorPicker;
|
||||||
|
import com.jfoenix.controls.JFXTextField;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.event.EventHandler;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class GuiPreferencesDialog extends StageDialog {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
@FXML private JFXTextField xmlFilePathTextField;
|
||||||
|
@FXML private JFXTextField mobilityFilePathTextField;
|
||||||
|
@FXML private JFXTextField shellCommandTextField;
|
||||||
|
@FXML private JFXTextField iconPathTextField;
|
||||||
|
@FXML private JFXColorPicker nodeLabelColorPicker;
|
||||||
|
@FXML private JFXColorPicker nodeLabelBackgroundColorPicker;
|
||||||
|
@FXML private JFXTextField throughputLimitTextField;
|
||||||
|
@FXML private JFXTextField throughputWidthTextField;
|
||||||
|
@FXML private JFXButton saveButton;
|
||||||
|
|
||||||
|
public GuiPreferencesDialog(Controller controller) {
|
||||||
|
super(controller, "/fxml/gui_preferences.fxml");
|
||||||
|
setTitle("GUI Preferences");
|
||||||
|
saveButton = createButton("Save");
|
||||||
|
saveButton.setOnAction(onSave);
|
||||||
|
addCancelButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
private EventHandler<ActionEvent> onSave = event -> {
|
||||||
|
Configuration configuration = getController().getConfiguration();
|
||||||
|
configuration.setXmlPath(xmlFilePathTextField.getText());
|
||||||
|
configuration.setMobilityPath(mobilityFilePathTextField.getText());
|
||||||
|
configuration.setShellCommand(shellCommandTextField.getText());
|
||||||
|
configuration.setIconPath(iconPathTextField.getText());
|
||||||
|
configuration.setNodeLabelColor(nodeLabelColorPicker.getValue().toString());
|
||||||
|
configuration.setNodeLabelBackgroundColor(nodeLabelBackgroundColorPicker.getValue().toString());
|
||||||
|
configuration.setThroughputLimit(Double.parseDouble(throughputLimitTextField.getText()));
|
||||||
|
configuration.setThroughputWidth(Integer.parseInt(throughputWidthTextField.getText()));
|
||||||
|
getController().getNetworkGraph().updatePreferences(configuration);
|
||||||
|
try {
|
||||||
|
ConfigUtils.save(configuration);
|
||||||
|
Toast.success("Updated preferences");
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Toast.error("Failure to update preferences", ex);
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
};
|
||||||
|
|
||||||
|
public void showDialog() {
|
||||||
|
Configuration configuration = getController().getConfiguration();
|
||||||
|
xmlFilePathTextField.setText(configuration.getXmlPath());
|
||||||
|
mobilityFilePathTextField.setText(configuration.getMobilityPath());
|
||||||
|
shellCommandTextField.setText(configuration.getShellCommand());
|
||||||
|
iconPathTextField.setText(configuration.getIconPath());
|
||||||
|
nodeLabelColorPicker.setValue(Color.web(configuration.getNodeLabelColor()));
|
||||||
|
nodeLabelBackgroundColorPicker.setValue(Color.web(configuration.getNodeLabelBackgroundColor()));
|
||||||
|
String throughputLimit = null;
|
||||||
|
if (configuration.getThroughputLimit() != null) {
|
||||||
|
throughputLimit = configuration.getThroughputLimit().toString();
|
||||||
|
}
|
||||||
|
throughputLimitTextField.setText(throughputLimit);
|
||||||
|
|
||||||
|
String throughputWidth = null;
|
||||||
|
if (configuration.getThroughputWidth() != null) {
|
||||||
|
throughputWidth = configuration.getThroughputWidth().toString();
|
||||||
|
}
|
||||||
|
throughputWidthTextField.setText(throughputWidth);
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
}
|
77
corefx/src/main/java/com/core/ui/dialogs/HookDialog.java
Normal file
77
corefx/src/main/java/com/core/ui/dialogs/HookDialog.java
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
package com.core.ui.dialogs;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.data.Hook;
|
||||||
|
import com.core.data.SessionState;
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import com.jfoenix.controls.JFXComboBox;
|
||||||
|
import com.jfoenix.controls.JFXTextArea;
|
||||||
|
import com.jfoenix.controls.JFXTextField;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class HookDialog extends StageDialog {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private static final String DEFAULT_DATA = "#!/bin/sh\n" +
|
||||||
|
"# session hook script; write commands here to execute\n" +
|
||||||
|
"# on the host at the specified state\n";
|
||||||
|
@FXML private JFXComboBox<String> stateCombo;
|
||||||
|
@FXML private JFXTextField fileTextField;
|
||||||
|
@FXML private JFXTextArea fileData;
|
||||||
|
private JFXButton saveButton;
|
||||||
|
|
||||||
|
public HookDialog(Controller controller) {
|
||||||
|
super(controller, "/fxml/hook_dialog.fxml");
|
||||||
|
setTitle("Hook");
|
||||||
|
saveButton = createButton("Save");
|
||||||
|
addCancelButton();
|
||||||
|
|
||||||
|
stateCombo.getItems()
|
||||||
|
.addAll(Arrays.stream(SessionState.values()).map(Enum::name).sorted().collect(Collectors.toList()));
|
||||||
|
stateCombo.getSelectionModel().select(SessionState.RUNTIME.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Hook getHook() {
|
||||||
|
Hook hook = new Hook();
|
||||||
|
hook.setFile(fileTextField.getText());
|
||||||
|
hook.setData(fileData.getText());
|
||||||
|
SessionState state = SessionState.valueOf(stateCombo.getSelectionModel().getSelectedItem());
|
||||||
|
hook.setState(state.getValue());
|
||||||
|
hook.setStateDisplay(state.name());
|
||||||
|
return hook;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showEditDialog(Hook hook, Runnable editHandler, Runnable cancelHandler) {
|
||||||
|
fileData.setText(hook.getData());
|
||||||
|
stateCombo.getSelectionModel().select(hook.getState());
|
||||||
|
fileTextField.setText(hook.getFile());
|
||||||
|
fileTextField.setDisable(true);
|
||||||
|
|
||||||
|
saveButton.setOnAction(event -> {
|
||||||
|
logger.info("create hook");
|
||||||
|
editHandler.run();
|
||||||
|
close();
|
||||||
|
});
|
||||||
|
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showDialog(String fileName, Runnable saveHandler, Runnable cancelHandler) {
|
||||||
|
fileData.setText(DEFAULT_DATA);
|
||||||
|
stateCombo.getSelectionModel().select(SessionState.RUNTIME.name());
|
||||||
|
fileTextField.setText(fileName);
|
||||||
|
fileTextField.setDisable(false);
|
||||||
|
|
||||||
|
saveButton.setOnAction(event -> {
|
||||||
|
logger.info("create hook");
|
||||||
|
saveHandler.run();
|
||||||
|
close();
|
||||||
|
});
|
||||||
|
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
}
|
117
corefx/src/main/java/com/core/ui/dialogs/HooksDialog.java
Normal file
117
corefx/src/main/java/com/core/ui/dialogs/HooksDialog.java
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
package com.core.ui.dialogs;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.data.Hook;
|
||||||
|
import com.core.data.SessionState;
|
||||||
|
import com.core.ui.Toast;
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.TableColumn;
|
||||||
|
import javafx.scene.control.TableView;
|
||||||
|
import javafx.scene.control.cell.PropertyValueFactory;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class HooksDialog extends StageDialog {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private HookDialog hookDialog;
|
||||||
|
private int fileCount = 0;
|
||||||
|
@FXML private TableView<Hook> hooksTable;
|
||||||
|
@FXML private TableColumn<Hook, Integer> fileColumn;
|
||||||
|
@FXML private TableColumn<Hook, Integer> stateColumn;
|
||||||
|
|
||||||
|
public HooksDialog(Controller controller) {
|
||||||
|
super(controller, "/fxml/hooks_dialog.fxml");
|
||||||
|
hookDialog = new HookDialog(controller);
|
||||||
|
|
||||||
|
setTitle("Hooks");
|
||||||
|
|
||||||
|
JFXButton createButton = createButton("Create");
|
||||||
|
createButton.setOnAction(event -> {
|
||||||
|
logger.info("showing create hook");
|
||||||
|
hookDialog.showDialog(nextFile(), saveHandler, cancelHandler);
|
||||||
|
});
|
||||||
|
JFXButton editButton = createButton("Edit");
|
||||||
|
editButton.setDisable(true);
|
||||||
|
editButton.setOnAction(event -> {
|
||||||
|
logger.info("edit hook");
|
||||||
|
Hook hook = hooksTable.getSelectionModel().getSelectedItem();
|
||||||
|
hookDialog.showEditDialog(hook, editHandler, cancelHandler);
|
||||||
|
});
|
||||||
|
JFXButton deleteButton = createButton("Delete");
|
||||||
|
deleteButton.setDisable(true);
|
||||||
|
deleteButton.setOnAction(event -> {
|
||||||
|
logger.info("delete hook");
|
||||||
|
Hook hook = hooksTable.getSelectionModel().getSelectedItem();
|
||||||
|
hooksTable.getItems().remove(hook);
|
||||||
|
});
|
||||||
|
|
||||||
|
addCancelButton();
|
||||||
|
|
||||||
|
hooksTable.getSelectionModel().selectedItemProperty().addListener((ov, old, current) -> {
|
||||||
|
boolean hasNoSelection = current == null;
|
||||||
|
editButton.setDisable(hasNoSelection);
|
||||||
|
deleteButton.setDisable(hasNoSelection);
|
||||||
|
});
|
||||||
|
|
||||||
|
fileColumn.setCellValueFactory(new PropertyValueFactory<>("file"));
|
||||||
|
stateColumn.setCellValueFactory(new PropertyValueFactory<>("stateDisplay"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOwner(Stage window) {
|
||||||
|
super.setOwner(window);
|
||||||
|
hookDialog.setOwner(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Runnable saveHandler = () -> {
|
||||||
|
Hook hook = hookDialog.getHook();
|
||||||
|
hooksTable.getItems().addAll(hook);
|
||||||
|
};
|
||||||
|
|
||||||
|
private Runnable editHandler = () -> {
|
||||||
|
Hook hook = hooksTable.getSelectionModel().getSelectedItem();
|
||||||
|
Hook update = hookDialog.getHook();
|
||||||
|
SessionState state = SessionState.valueOf(update.getStateDisplay());
|
||||||
|
hook.setState(state.getValue());
|
||||||
|
hook.setData(update.getData());
|
||||||
|
};
|
||||||
|
|
||||||
|
private Runnable cancelHandler = this::showDialog;
|
||||||
|
|
||||||
|
private String nextFile() {
|
||||||
|
return String.format("file%s.sh", ++fileCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Hook> getHooks() {
|
||||||
|
return hooksTable.getItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateHooks() {
|
||||||
|
logger.info("updating hooks");
|
||||||
|
hooksTable.getItems().clear();
|
||||||
|
|
||||||
|
// update hooks
|
||||||
|
try {
|
||||||
|
List<Hook> hooks = getCoreClient().getHooks();
|
||||||
|
for (Hook hook : hooks) {
|
||||||
|
SessionState state = SessionState.get(hook.getState());
|
||||||
|
hook.setStateDisplay(state.name());
|
||||||
|
hooksTable.getItems().add(hook);
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.error("error getting current hooks", ex);
|
||||||
|
Toast.error("Error getting current hooks");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showDialog() {
|
||||||
|
// clear current selection
|
||||||
|
hooksTable.getSelectionModel().clearSelection();
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
}
|
73
corefx/src/main/java/com/core/ui/dialogs/LocationDialog.java
Normal file
73
corefx/src/main/java/com/core/ui/dialogs/LocationDialog.java
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
package com.core.ui.dialogs;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.data.LocationConfig;
|
||||||
|
import com.core.ui.Toast;
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import com.jfoenix.controls.JFXTextField;
|
||||||
|
import com.jfoenix.validation.DoubleValidator;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class LocationDialog extends StageDialog {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
@FXML private JFXTextField scaleTextField;
|
||||||
|
@FXML private JFXTextField xTextField;
|
||||||
|
@FXML private JFXTextField yTextField;
|
||||||
|
@FXML private JFXTextField latTextField;
|
||||||
|
@FXML private JFXTextField lonTextField;
|
||||||
|
@FXML private JFXTextField altTextField;
|
||||||
|
private JFXButton saveButton;
|
||||||
|
|
||||||
|
public LocationDialog(Controller controller) {
|
||||||
|
super(controller, "/fxml/location_dialog.fxml");
|
||||||
|
setTitle("Location Configuration");
|
||||||
|
saveButton = createButton("Save");
|
||||||
|
saveButton.setOnAction(event -> {
|
||||||
|
boolean result = scaleTextField.validate();
|
||||||
|
if (!result) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LocationConfig config = new LocationConfig();
|
||||||
|
config.setScale(getDouble(scaleTextField));
|
||||||
|
config.getPosition().setX(getDouble(xTextField));
|
||||||
|
config.getPosition().setY(getDouble(yTextField));
|
||||||
|
config.getLocation().setLatitude(getDouble(latTextField));
|
||||||
|
config.getLocation().setLongitude(getDouble(lonTextField));
|
||||||
|
config.getLocation().setAltitude(getDouble(altTextField));
|
||||||
|
try {
|
||||||
|
getCoreClient().setLocationConfig(config);
|
||||||
|
close();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Toast.error("error setting location config", ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
addCancelButton();
|
||||||
|
|
||||||
|
DoubleValidator validator = new DoubleValidator();
|
||||||
|
scaleTextField.getValidators().add(validator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getDouble(JFXTextField textField) {
|
||||||
|
return Double.parseDouble(textField.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showDialog() {
|
||||||
|
try {
|
||||||
|
LocationConfig config = getCoreClient().getLocationConfig();
|
||||||
|
scaleTextField.setText(config.getScale().toString());
|
||||||
|
xTextField.setText(config.getPosition().getX().toString());
|
||||||
|
yTextField.setText(config.getPosition().getY().toString());
|
||||||
|
latTextField.setText(config.getLocation().getLatitude().toString());
|
||||||
|
lonTextField.setText(config.getLocation().getLongitude().toString());
|
||||||
|
altTextField.setText(config.getLocation().getAltitude().toString());
|
||||||
|
show();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Toast.error("error getting location config", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
113
corefx/src/main/java/com/core/ui/dialogs/MobilityDialog.java
Normal file
113
corefx/src/main/java/com/core/ui/dialogs/MobilityDialog.java
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
package com.core.ui.dialogs;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.data.CoreNode;
|
||||||
|
import com.core.data.MobilityConfig;
|
||||||
|
import com.core.ui.Toast;
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import com.jfoenix.controls.JFXTextField;
|
||||||
|
import com.jfoenix.controls.JFXToggleButton;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.layout.GridPane;
|
||||||
|
import javafx.stage.FileChooser;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class MobilityDialog extends StageDialog {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
@FXML private JFXTextField fileTextField;
|
||||||
|
@FXML private JFXTextField refreshTextField;
|
||||||
|
@FXML private JFXToggleButton loopToggleButton;
|
||||||
|
@FXML private JFXTextField nodeMappingTextField;
|
||||||
|
@FXML private JFXTextField autoStartTextField;
|
||||||
|
@FXML private JFXTextField startTextField;
|
||||||
|
@FXML private JFXTextField pauseTextField;
|
||||||
|
@FXML private JFXTextField stopTextField;
|
||||||
|
private CoreNode node;
|
||||||
|
|
||||||
|
public MobilityDialog(Controller controller) {
|
||||||
|
super(controller, "/fxml/mobility_dialog.fxml");
|
||||||
|
setTitle("Mobility Script");
|
||||||
|
|
||||||
|
JFXButton saveButton = createButton("Save");
|
||||||
|
saveButton.setOnAction(event -> {
|
||||||
|
MobilityConfig mobilityConfig = new MobilityConfig();
|
||||||
|
mobilityConfig.setFile(fileTextField.getText());
|
||||||
|
mobilityConfig.setScriptFile(new File(mobilityConfig.getFile()));
|
||||||
|
mobilityConfig.setAutostart(autoStartTextField.getText());
|
||||||
|
String loop = loopToggleButton.isSelected() ? "1" : "";
|
||||||
|
mobilityConfig.setLoop(loop);
|
||||||
|
mobilityConfig.setRefresh(Integer.parseInt(refreshTextField.getText()));
|
||||||
|
mobilityConfig.setMap(nodeMappingTextField.getText());
|
||||||
|
mobilityConfig.setStartScript(startTextField.getText());
|
||||||
|
mobilityConfig.setPauseScript(pauseTextField.getText());
|
||||||
|
mobilityConfig.setStopScript(stopTextField.getText());
|
||||||
|
|
||||||
|
try {
|
||||||
|
boolean result = controller.getCoreClient().setMobilityConfig(node, mobilityConfig);
|
||||||
|
if (result) {
|
||||||
|
getController().getMobilityScripts().put(node.getId(), mobilityConfig);
|
||||||
|
Toast.info(String.format("Set mobility configuration for %s", node.getName()));
|
||||||
|
} else {
|
||||||
|
Toast.error(String.format("Error setting mobility configuration for %s", node.getName()));
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Toast.error("error setting mobility configuration", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
close();
|
||||||
|
});
|
||||||
|
addCancelButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void onSelectAction(ActionEvent event) {
|
||||||
|
JFXButton button = (JFXButton) event.getSource();
|
||||||
|
GridPane gridPane = (GridPane) button.getParent();
|
||||||
|
JFXTextField textField = (JFXTextField) gridPane.getChildren().get(0);
|
||||||
|
|
||||||
|
FileChooser fileChooser = new FileChooser();
|
||||||
|
fileChooser.setTitle("Select File");
|
||||||
|
String mobilityPath = getController().getConfiguration().getMobilityPath();
|
||||||
|
fileChooser.setInitialDirectory(new File(mobilityPath));
|
||||||
|
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Mobility",
|
||||||
|
"*.scen"));
|
||||||
|
try {
|
||||||
|
File file = fileChooser.showOpenDialog(getController().getWindow());
|
||||||
|
if (file != null) {
|
||||||
|
logger.info("opening session xml: {}", file.getPath());
|
||||||
|
textField.setText(file.getPath());
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
Toast.error(String.format("Invalid mobility directory: %s",
|
||||||
|
getController().getConfiguration().getMobilityPath()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showDialog(CoreNode node) {
|
||||||
|
this.node = node;
|
||||||
|
|
||||||
|
try {
|
||||||
|
MobilityConfig mobilityConfig = getController().getCoreClient().getMobilityConfig(this.node);
|
||||||
|
fileTextField.setText(mobilityConfig.getFile());
|
||||||
|
autoStartTextField.setText(mobilityConfig.getAutostart());
|
||||||
|
boolean loop = "1".equals(mobilityConfig.getLoop());
|
||||||
|
loopToggleButton.setSelected(loop);
|
||||||
|
refreshTextField.setText(mobilityConfig.getRefresh().toString());
|
||||||
|
nodeMappingTextField.setText(mobilityConfig.getMap());
|
||||||
|
startTextField.setText(mobilityConfig.getStartScript());
|
||||||
|
pauseTextField.setText(mobilityConfig.getPauseScript());
|
||||||
|
stopTextField.setText(mobilityConfig.getStopScript());
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Toast.error("error getting mobility config", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
package com.core.ui.dialogs;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.data.CoreNode;
|
||||||
|
import com.core.data.MobilityConfig;
|
||||||
|
import com.core.data.SessionState;
|
||||||
|
import com.core.ui.Toast;
|
||||||
|
import com.core.utils.IconUtils;
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import com.jfoenix.controls.JFXProgressBar;
|
||||||
|
import javafx.animation.KeyFrame;
|
||||||
|
import javafx.animation.KeyValue;
|
||||||
|
import javafx.animation.Timeline;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.stage.Modality;
|
||||||
|
import javafx.util.Duration;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class MobilityPlayerDialog extends StageDialog {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private static final int ICON_SIZE = 20;
|
||||||
|
private static final String ICON_FILL = "white";
|
||||||
|
@FXML private Label label;
|
||||||
|
@FXML private JFXButton playButton;
|
||||||
|
@FXML private JFXButton pauseButton;
|
||||||
|
@FXML private JFXButton stopButton;
|
||||||
|
@FXML private JFXProgressBar progressBar;
|
||||||
|
private final CoreNode node;
|
||||||
|
private MobilityConfig mobilityConfig;
|
||||||
|
|
||||||
|
public MobilityPlayerDialog(Controller controller, CoreNode node) {
|
||||||
|
super(controller, "/fxml/mobility_player.fxml", Modality.NONE);
|
||||||
|
this.node = node;
|
||||||
|
|
||||||
|
playButton.setGraphic(IconUtils.get("play_arrow", ICON_SIZE, ICON_FILL));
|
||||||
|
playButton.setOnAction(event -> action("start"));
|
||||||
|
pauseButton.setGraphic(IconUtils.get("pause", ICON_SIZE, ICON_FILL));
|
||||||
|
pauseButton.setOnAction(event -> action("pause"));
|
||||||
|
stopButton.setGraphic(IconUtils.get("stop", ICON_SIZE, ICON_FILL));
|
||||||
|
stopButton.setOnAction(event -> action("stop"));
|
||||||
|
|
||||||
|
addCancelButton();
|
||||||
|
setTitle(String.format("%s Mobility Script", node.getName()));
|
||||||
|
getStage().sizeToScene();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void event(SessionState state, Integer start, Integer end) {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
playButton.setDisable(false);
|
||||||
|
stopButton.setDisable(false);
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case START:
|
||||||
|
playButton.setDisable(true);
|
||||||
|
progressBar.setProgress(0);
|
||||||
|
Timeline timeline = new Timeline();
|
||||||
|
KeyValue keyValue = new KeyValue(progressBar.progressProperty(), 1.0);
|
||||||
|
KeyFrame keyFrame = new KeyFrame(new Duration(end * 1000), keyValue);
|
||||||
|
timeline.getKeyFrames().add(keyFrame);
|
||||||
|
timeline.play();
|
||||||
|
break;
|
||||||
|
case STOP:
|
||||||
|
stopButton.setDisable(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void action(String action) {
|
||||||
|
try {
|
||||||
|
getCoreClient().mobilityAction(node, action);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Toast.error(String.format("mobility error: %s", action), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showDialog(MobilityConfig mobilityConfig) {
|
||||||
|
this.label.setText(mobilityConfig.getFile());
|
||||||
|
this.mobilityConfig = mobilityConfig;
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
}
|
103
corefx/src/main/java/com/core/ui/dialogs/NodeEmaneDialog.java
Normal file
103
corefx/src/main/java/com/core/ui/dialogs/NodeEmaneDialog.java
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
package com.core.ui.dialogs;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.data.ConfigGroup;
|
||||||
|
import com.core.data.ConfigOption;
|
||||||
|
import com.core.data.CoreNode;
|
||||||
|
import com.core.ui.Toast;
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import com.jfoenix.controls.JFXComboBox;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class NodeEmaneDialog extends StageDialog {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private final JFXButton saveButton;
|
||||||
|
private CoreNode coreNode;
|
||||||
|
@FXML private JFXComboBox<String> modelCombo;
|
||||||
|
@FXML private JFXButton modelButton;
|
||||||
|
@FXML private JFXButton emaneButton;
|
||||||
|
|
||||||
|
public NodeEmaneDialog(Controller controller) {
|
||||||
|
super(controller, "/fxml/node_emane_dialog.fxml");
|
||||||
|
|
||||||
|
saveButton = createButton("Save");
|
||||||
|
saveButton.setOnAction(event -> {
|
||||||
|
String model = modelCombo.getSelectionModel().getSelectedItem();
|
||||||
|
coreNode.setEmane(model);
|
||||||
|
close();
|
||||||
|
});
|
||||||
|
addCancelButton();
|
||||||
|
|
||||||
|
emaneButton.setOnAction(this::emaneButtonHandler);
|
||||||
|
modelButton.setOnAction(this::emaneModelButtonHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setModels(List<String> models) {
|
||||||
|
models.sort(String::compareTo);
|
||||||
|
modelCombo.getItems().setAll(models);
|
||||||
|
modelCombo.getSelectionModel().selectFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getModels() {
|
||||||
|
return modelCombo.getItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void emaneButtonHandler(ActionEvent event) {
|
||||||
|
try {
|
||||||
|
List<ConfigGroup> configGroups = getCoreClient().getEmaneConfig(coreNode);
|
||||||
|
logger.debug("emane model config: {}", configGroups);
|
||||||
|
String title = String.format("%s EMANE Config", coreNode.getName());
|
||||||
|
getController().getConfigDialog().showDialog(title, configGroups, () -> {
|
||||||
|
List<ConfigOption> options = getController().getConfigDialog().getOptions();
|
||||||
|
try {
|
||||||
|
getCoreClient().setEmaneConfig(coreNode, options);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.error("set emane config error", ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Toast.error("error getting emane model config", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void emaneModelButtonHandler(ActionEvent event) {
|
||||||
|
String model = modelCombo.getSelectionModel().getSelectedItem();
|
||||||
|
displayEmaneModelConfig(coreNode.getId(), model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void displayEmaneModelConfig(Integer id, String model) {
|
||||||
|
try {
|
||||||
|
List<ConfigGroup> configGroups = getCoreClient().getEmaneModelConfig(id, model);
|
||||||
|
logger.debug("emane model config: {}", configGroups);
|
||||||
|
String title = String.format("EMANE(%s) %s Config", id, model);
|
||||||
|
getController().getConfigDialog().showDialog(title, configGroups, () -> {
|
||||||
|
List<ConfigOption> options = getController().getConfigDialog().getOptions();
|
||||||
|
try {
|
||||||
|
getCoreClient().setEmaneModelConfig(id, model, options);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Toast.error("set emane model config error", ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Toast.error("error getting emane model config", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setDisabled(boolean isDisabled) {
|
||||||
|
saveButton.setDisable(isDisabled);
|
||||||
|
modelCombo.setDisable(isDisabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showDialog(CoreNode node) {
|
||||||
|
coreNode = node;
|
||||||
|
setTitle(String.format("%s - EMANE", node.getName()));
|
||||||
|
setDisabled(getCoreClient().isRunning());
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
}
|
148
corefx/src/main/java/com/core/ui/dialogs/NodeServicesDialog.java
Normal file
148
corefx/src/main/java/com/core/ui/dialogs/NodeServicesDialog.java
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
package com.core.ui.dialogs;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.data.CoreNode;
|
||||||
|
import com.core.ui.ServiceItem;
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import com.jfoenix.controls.JFXListView;
|
||||||
|
import com.jfoenix.controls.JFXScrollPane;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.ScrollPane;
|
||||||
|
import javafx.scene.layout.GridPane;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class NodeServicesDialog extends StageDialog {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private final Map<String, List<ServiceItem>> serviceItemGroups = new HashMap<>();
|
||||||
|
private final Map<String, ServiceItem> serviceItemMap = new HashMap<>();
|
||||||
|
private CoreNode node;
|
||||||
|
private int index = 0;
|
||||||
|
@FXML private GridPane gridPane;
|
||||||
|
@FXML private ScrollPane scrollPane;
|
||||||
|
@FXML private JFXListView<String> groupListView;
|
||||||
|
@FXML private JFXListView<String> activeListView;
|
||||||
|
@FXML private JFXButton removeButton;
|
||||||
|
@FXML private JFXButton editButton;
|
||||||
|
|
||||||
|
public NodeServicesDialog(Controller controller) {
|
||||||
|
super(controller, "/fxml/node_services_dialog.fxml");
|
||||||
|
|
||||||
|
JFXButton saveButton = createButton("Save");
|
||||||
|
saveButton.setOnAction(event -> {
|
||||||
|
for (List<ServiceItem> items : serviceItemGroups.values()) {
|
||||||
|
for (ServiceItem item : items) {
|
||||||
|
if (item.getCheckBox().isSelected()) {
|
||||||
|
logger.info("setting service for node({}): {}", node.getName(), item.getService());
|
||||||
|
node.getServices().add(item.getService());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
});
|
||||||
|
addCancelButton();
|
||||||
|
|
||||||
|
groupListView.getSelectionModel().selectedItemProperty().addListener((ov, previous, current) -> {
|
||||||
|
if (current == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateItems(current);
|
||||||
|
});
|
||||||
|
|
||||||
|
activeListView.getSelectionModel().selectedItemProperty().addListener((ov, previous, current) -> {
|
||||||
|
boolean isDisabled = current == null;
|
||||||
|
removeButton.setDisable(isDisabled);
|
||||||
|
editButton.setDisable(isDisabled);
|
||||||
|
});
|
||||||
|
|
||||||
|
removeButton.setOnAction(event -> {
|
||||||
|
String service = activeListView.getSelectionModel().getSelectedItem();
|
||||||
|
activeListView.getItems().remove(service);
|
||||||
|
ServiceItem serviceItem = serviceItemMap.get(service);
|
||||||
|
serviceItem.getCheckBox().setSelected(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
editButton.setOnAction(event -> {
|
||||||
|
String service = activeListView.getSelectionModel().getSelectedItem();
|
||||||
|
getController().getServiceDialog().showDialog(node, service);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServices(Map<String, List<String>> serviceGroups) {
|
||||||
|
serviceItemGroups.clear();
|
||||||
|
|
||||||
|
serviceGroups.keySet().stream()
|
||||||
|
.sorted()
|
||||||
|
.forEach(group -> {
|
||||||
|
groupListView.getItems().add(group);
|
||||||
|
serviceGroups.get(group).stream()
|
||||||
|
.sorted()
|
||||||
|
.forEach(service -> {
|
||||||
|
ServiceItem serviceItem = new ServiceItem(service);
|
||||||
|
List<ServiceItem> items = serviceItemGroups.computeIfAbsent(
|
||||||
|
group, k -> new ArrayList<>());
|
||||||
|
items.add(serviceItem);
|
||||||
|
|
||||||
|
if (serviceItem.getCheckBox().isSelected()) {
|
||||||
|
activeListView.getItems().add(serviceItem.getService());
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceItem.getCheckBox().setOnAction(event -> {
|
||||||
|
if (serviceItem.getCheckBox().isSelected()) {
|
||||||
|
activeListView.getItems().add(service);
|
||||||
|
FXCollections.sort(activeListView.getItems());
|
||||||
|
} else {
|
||||||
|
activeListView.getItems().remove(service);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
serviceItemMap.put(service, serviceItem);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
groupListView.getSelectionModel().selectFirst();
|
||||||
|
JFXScrollPane.smoothScrolling(scrollPane);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateItems(String group) {
|
||||||
|
logger.debug("updating services for group: {}", group);
|
||||||
|
clearAvailableServices();
|
||||||
|
List<ServiceItem> items = serviceItemGroups.get(group);
|
||||||
|
for (ServiceItem item : items) {
|
||||||
|
gridPane.addRow(index++, item.getCheckBox());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearAvailableServices() {
|
||||||
|
gridPane.getChildren().clear();
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showDialog(CoreNode node) {
|
||||||
|
this.node = node;
|
||||||
|
setTitle(String.format("%s - Services", node.getName()));
|
||||||
|
groupListView.getSelectionModel().selectFirst();
|
||||||
|
activeListView.getItems().clear();
|
||||||
|
|
||||||
|
Set<String> nodeServices = node.getServices();
|
||||||
|
if (nodeServices.isEmpty()) {
|
||||||
|
nodeServices = getController().getDefaultServices().get(node.getModel());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (List<ServiceItem> items : serviceItemGroups.values()) {
|
||||||
|
for (ServiceItem item : items) {
|
||||||
|
boolean selected = nodeServices.contains(item.getService());
|
||||||
|
item.getCheckBox().setSelected(selected);
|
||||||
|
if (item.getCheckBox().isSelected()) {
|
||||||
|
activeListView.getItems().add(item.getService());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FXCollections.sort(activeListView.getItems());
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
package com.core.ui.dialogs;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.data.NodeType;
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import com.jfoenix.controls.JFXListView;
|
||||||
|
import com.jfoenix.controls.JFXTextField;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.SelectionMode;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class NodeTypeCreateDialog extends StageDialog {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
@FXML private JFXListView<String> servicesListView;
|
||||||
|
@FXML private JFXTextField modelTextField;
|
||||||
|
@FXML private JFXTextField displayTextField;
|
||||||
|
private Runnable onCreateHandler;
|
||||||
|
|
||||||
|
public NodeTypeCreateDialog(Controller controller) {
|
||||||
|
super(controller, "/fxml/node_type_create_dialog.fxml");
|
||||||
|
setTitle("Create Node Configuration");
|
||||||
|
|
||||||
|
JFXButton saveButton = createButton("Create");
|
||||||
|
saveButton.setOnAction(event -> {
|
||||||
|
onCreateHandler.run();
|
||||||
|
close();
|
||||||
|
});
|
||||||
|
addCancelButton();
|
||||||
|
|
||||||
|
servicesListView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
|
||||||
|
displayTextField.focusedProperty().addListener((obs, prev, current) -> {
|
||||||
|
if (!current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String model = modelTextField.getText();
|
||||||
|
if (!model.isEmpty()) {
|
||||||
|
displayTextField.setText(model.substring(0, 1).toUpperCase() + model.substring(1));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public NodeType getCreatedNodeType() {
|
||||||
|
NodeType nodeType = new NodeType(NodeType.DEFAULT, modelTextField.getText(), displayTextField.getText(),
|
||||||
|
"/icons/host-100.png");
|
||||||
|
nodeType.getServices().addAll(servicesListView.getSelectionModel().getSelectedItems());
|
||||||
|
return nodeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServices(Map<String, List<String>> serviceGroups) {
|
||||||
|
List<String> services = new ArrayList<>();
|
||||||
|
for (List<String> groupServices : serviceGroups.values()) {
|
||||||
|
services.addAll(groupServices);
|
||||||
|
}
|
||||||
|
services.sort(String::compareTo);
|
||||||
|
servicesListView.getItems().setAll(services);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void showDialog(Runnable runnable) {
|
||||||
|
onCreateHandler = runnable;
|
||||||
|
modelTextField.setText("");
|
||||||
|
displayTextField.setText("");
|
||||||
|
servicesListView.getSelectionModel().clearSelection();
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
}
|
136
corefx/src/main/java/com/core/ui/dialogs/NodeTypesDialog.java
Normal file
136
corefx/src/main/java/com/core/ui/dialogs/NodeTypesDialog.java
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
package com.core.ui.dialogs;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.data.CoreNode;
|
||||||
|
import com.core.data.NodeType;
|
||||||
|
import com.core.ui.Toast;
|
||||||
|
import com.core.utils.NodeTypeConfig;
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import com.jfoenix.controls.JFXListView;
|
||||||
|
import com.jfoenix.controls.JFXTextField;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.stage.FileChooser;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class NodeTypesDialog extends StageDialog {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private final Map<String, NodeType> nodeTypeMap = new HashMap<>();
|
||||||
|
private NodeType selectedNodeType;
|
||||||
|
@FXML private JFXListView<String> listView;
|
||||||
|
@FXML private JFXTextField modelTextField;
|
||||||
|
@FXML private JFXTextField displayTextField;
|
||||||
|
@FXML private JFXTextField iconTextField;
|
||||||
|
@FXML private JFXButton iconButton;
|
||||||
|
@FXML private ImageView iconImage;
|
||||||
|
@FXML private JFXButton saveButton;
|
||||||
|
@FXML private JFXButton addButton;
|
||||||
|
@FXML private JFXButton deleteButton;
|
||||||
|
@FXML private JFXListView<String> nodeServicesListView;
|
||||||
|
|
||||||
|
public NodeTypesDialog(Controller controller) {
|
||||||
|
super(controller, "/fxml/node_types_dialog.fxml");
|
||||||
|
setTitle("Node Configuration");
|
||||||
|
JFXButton closeButton = createButton("Close");
|
||||||
|
closeButton.setOnAction(event -> close());
|
||||||
|
|
||||||
|
listView.getSelectionModel().selectedItemProperty().addListener((ov, prev, current) -> {
|
||||||
|
if (current == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeType nodeType = nodeTypeMap.get(current);
|
||||||
|
modelTextField.setText(nodeType.getModel());
|
||||||
|
displayTextField.setText(nodeType.getDisplay());
|
||||||
|
iconTextField.setText(nodeType.getIcon());
|
||||||
|
iconImage.setImage(new Image(nodeType.getIcon()));
|
||||||
|
selectedNodeType = nodeType;
|
||||||
|
Set<String> services = nodeType.getServices();
|
||||||
|
nodeServicesListView.getItems().setAll(services);
|
||||||
|
});
|
||||||
|
|
||||||
|
iconButton.setOnAction(event -> {
|
||||||
|
FileChooser fileChooser = new FileChooser();
|
||||||
|
fileChooser.setTitle("Select Icon");
|
||||||
|
fileChooser.setInitialDirectory(new File(getController().getConfiguration().getIconPath()));
|
||||||
|
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("PNG", "*.png"));
|
||||||
|
File file = fileChooser.showOpenDialog(controller.getWindow());
|
||||||
|
if (file != null) {
|
||||||
|
String uri = file.toURI().toString();
|
||||||
|
iconImage.setImage(new Image(uri));
|
||||||
|
iconTextField.setText(uri);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
saveButton.setOnAction(event -> {
|
||||||
|
String iconPath = iconTextField.getText();
|
||||||
|
selectedNodeType.setIcon(iconPath);
|
||||||
|
for (CoreNode node : controller.getNetworkGraph().getGraph().getVertices()) {
|
||||||
|
if (selectedNodeType != node.getNodeType()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
node.setNodeType(selectedNodeType);
|
||||||
|
}
|
||||||
|
controller.getNetworkGraph().getGraphViewer().repaint();
|
||||||
|
controller.getGraphToolbar().updateNodeType(selectedNodeType.getId(), iconPath);
|
||||||
|
Toast.info(String.format("Node %s Updated", selectedNodeType.getDisplay()));
|
||||||
|
});
|
||||||
|
|
||||||
|
deleteButton.setOnAction(event -> {
|
||||||
|
String display = listView.getSelectionModel().getSelectedItem();
|
||||||
|
NodeType nodeType = nodeTypeMap.get(display);
|
||||||
|
NodeType.remove(nodeType);
|
||||||
|
listView.getItems().remove(display);
|
||||||
|
NodeTypeConfig nodeTypeConfig = createNodeTypeConfig(nodeType);
|
||||||
|
getController().getDefaultServices().remove(nodeTypeConfig.getModel());
|
||||||
|
getController().getConfiguration().getNodeTypeConfigs().remove(nodeTypeConfig);
|
||||||
|
getController().updateNodeTypes();
|
||||||
|
});
|
||||||
|
|
||||||
|
addButton.setOnAction(event -> {
|
||||||
|
NodeTypeCreateDialog nodeTypeCreateDialog = getController().getNodeTypeCreateDialog();
|
||||||
|
nodeTypeCreateDialog.showDialog(() -> {
|
||||||
|
NodeType nodeType = nodeTypeCreateDialog.getCreatedNodeType();
|
||||||
|
NodeType.add(nodeType);
|
||||||
|
nodeTypeMap.put(nodeType.getDisplay(), nodeType);
|
||||||
|
listView.getItems().add(nodeType.getDisplay());
|
||||||
|
NodeTypeConfig nodeTypeConfig = createNodeTypeConfig(nodeType);
|
||||||
|
getController().getDefaultServices().put(nodeTypeConfig.getModel(), nodeTypeConfig.getServices());
|
||||||
|
getController().getConfiguration().getNodeTypeConfigs().add(nodeTypeConfig);
|
||||||
|
getController().updateNodeTypes();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private NodeTypeConfig createNodeTypeConfig(NodeType nodeType) {
|
||||||
|
return new NodeTypeConfig(
|
||||||
|
nodeType.getModel(),
|
||||||
|
nodeType.getDisplay(),
|
||||||
|
nodeType.getIcon(),
|
||||||
|
nodeType.getServices()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showDialog() {
|
||||||
|
listView.getItems().clear();
|
||||||
|
nodeTypeMap.clear();
|
||||||
|
for (NodeType nodeType : NodeType.getAll()) {
|
||||||
|
if (nodeType.getValue() != NodeType.DEFAULT) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
nodeTypeMap.put(nodeType.getDisplay(), nodeType);
|
||||||
|
listView.getItems().add(nodeType.getDisplay());
|
||||||
|
}
|
||||||
|
listView.getSelectionModel().selectFirst();
|
||||||
|
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
}
|
76
corefx/src/main/java/com/core/ui/dialogs/NodeWlanDialog.java
Normal file
76
corefx/src/main/java/com/core/ui/dialogs/NodeWlanDialog.java
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
package com.core.ui.dialogs;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.client.rest.WlanConfig;
|
||||||
|
import com.core.data.CoreNode;
|
||||||
|
import com.core.ui.Toast;
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import com.jfoenix.controls.JFXTextField;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class NodeWlanDialog extends StageDialog {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private final JFXButton saveButton;
|
||||||
|
private CoreNode coreNode;
|
||||||
|
@FXML private JFXTextField rangeTextField;
|
||||||
|
@FXML private JFXTextField bandwidthTextField;
|
||||||
|
@FXML private JFXTextField delayTextField;
|
||||||
|
@FXML private JFXTextField lossTextField;
|
||||||
|
@FXML private JFXTextField jitterTextField;
|
||||||
|
|
||||||
|
public NodeWlanDialog(Controller controller) {
|
||||||
|
super(controller, "/fxml/wlan_dialog.fxml");
|
||||||
|
|
||||||
|
saveButton = createButton("Save");
|
||||||
|
saveButton.setOnAction(event -> {
|
||||||
|
try {
|
||||||
|
WlanConfig config = new WlanConfig();
|
||||||
|
config.setRange(rangeTextField.getText());
|
||||||
|
config.setBandwidth(bandwidthTextField.getText());
|
||||||
|
config.setJitter(jitterTextField.getText());
|
||||||
|
config.setDelay(delayTextField.getText());
|
||||||
|
config.setError(lossTextField.getText());
|
||||||
|
getCoreClient().setWlanConfig(coreNode, config);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.error("error setting wlan config", ex);
|
||||||
|
Toast.error("Error setting wlan config");
|
||||||
|
}
|
||||||
|
|
||||||
|
close();
|
||||||
|
});
|
||||||
|
addCancelButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setDisabled(boolean isDisabled) {
|
||||||
|
rangeTextField.setDisable(isDisabled);
|
||||||
|
bandwidthTextField.setDisable(isDisabled);
|
||||||
|
jitterTextField.setDisable(isDisabled);
|
||||||
|
delayTextField.setDisable(isDisabled);
|
||||||
|
lossTextField.setDisable(isDisabled);
|
||||||
|
saveButton.setDisable(isDisabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showDialog(CoreNode node) {
|
||||||
|
coreNode = node;
|
||||||
|
setTitle(String.format("%s - WLAN", node.getName()));
|
||||||
|
setDisabled(getCoreClient().isRunning());
|
||||||
|
|
||||||
|
try {
|
||||||
|
WlanConfig wlanConfig = getCoreClient().getWlanConfig(coreNode);
|
||||||
|
rangeTextField.setText(wlanConfig.getRange());
|
||||||
|
bandwidthTextField.setText(wlanConfig.getBandwidth());
|
||||||
|
jitterTextField.setText(wlanConfig.getJitter());
|
||||||
|
delayTextField.setText(wlanConfig.getDelay());
|
||||||
|
lossTextField.setText(wlanConfig.getError());
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.error("error getting wlan config", ex);
|
||||||
|
Toast.error("Error getting wlan config");
|
||||||
|
}
|
||||||
|
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
}
|
119
corefx/src/main/java/com/core/ui/dialogs/ServiceDialog.java
Normal file
119
corefx/src/main/java/com/core/ui/dialogs/ServiceDialog.java
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
package com.core.ui.dialogs;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.client.rest.ServiceFile;
|
||||||
|
import com.core.data.CoreNode;
|
||||||
|
import com.core.data.CoreService;
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import com.jfoenix.controls.JFXComboBox;
|
||||||
|
import com.jfoenix.controls.JFXTextArea;
|
||||||
|
import com.jfoenix.controls.JFXTextField;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class ServiceDialog extends StageDialog {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private CoreNode coreNode;
|
||||||
|
private CoreService coreService;
|
||||||
|
private String serviceName;
|
||||||
|
@FXML private JFXComboBox<String> executablesComboBox;
|
||||||
|
@FXML private JFXComboBox<String> dependenciesComboBox;
|
||||||
|
@FXML private JFXTextField validationModeTextField;
|
||||||
|
@FXML private JFXTextField validationTimerTextField;
|
||||||
|
@FXML private JFXComboBox<String> directoriesComboBox;
|
||||||
|
@FXML private JFXComboBox<String> filesComboBox;
|
||||||
|
@FXML private JFXTextArea fileTextArea;
|
||||||
|
@FXML private JFXTextArea startupTextArea;
|
||||||
|
@FXML private JFXTextArea validateTextArea;
|
||||||
|
@FXML private JFXTextArea shutdownTextArea;
|
||||||
|
|
||||||
|
public ServiceDialog(Controller controller) {
|
||||||
|
super(controller, "/fxml/service_dialog.fxml");
|
||||||
|
|
||||||
|
JFXButton saveButton = createButton("Save");
|
||||||
|
saveButton.setOnAction(event -> {
|
||||||
|
// retrieve service data
|
||||||
|
coreService.setStartup(textToList(startupTextArea.getText()));
|
||||||
|
coreService.setValidate(textToList(validateTextArea.getText()));
|
||||||
|
coreService.setShutdown(textToList(shutdownTextArea.getText()));
|
||||||
|
|
||||||
|
// service file data
|
||||||
|
String fileName = filesComboBox.getSelectionModel().getSelectedItem();
|
||||||
|
String data = fileTextArea.getText();
|
||||||
|
ServiceFile serviceFile = new ServiceFile(fileName, data);
|
||||||
|
|
||||||
|
try {
|
||||||
|
getCoreClient().setService(coreNode, serviceName, coreService);
|
||||||
|
getCoreClient().setServiceFile(coreNode, serviceName, serviceFile);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.error("error setting node service", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
close();
|
||||||
|
});
|
||||||
|
addCancelButton();
|
||||||
|
|
||||||
|
filesComboBox.valueProperty().addListener((ov, previous, current) -> {
|
||||||
|
if (current == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
String file = controller.getCoreClient().getServiceFile(coreNode, serviceName, current);
|
||||||
|
fileTextArea.setText(file);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.error("error getting file data", ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> textToList(String text) {
|
||||||
|
return Arrays.stream(text.split("\\n"))
|
||||||
|
.filter(x -> !x.isEmpty())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showDialog(CoreNode node, String service) {
|
||||||
|
setTitle(String.format("%s - %s", node.getName(), service));
|
||||||
|
|
||||||
|
try {
|
||||||
|
coreNode = node;
|
||||||
|
|
||||||
|
// node must exist to get file data
|
||||||
|
getCoreClient().createNode(node);
|
||||||
|
|
||||||
|
coreService = getCoreClient().getService(node, service);
|
||||||
|
logger.info("service dialog: {}", coreService);
|
||||||
|
serviceName = service;
|
||||||
|
|
||||||
|
directoriesComboBox.getItems().setAll(coreService.getDirs());
|
||||||
|
directoriesComboBox.getSelectionModel().selectFirst();
|
||||||
|
|
||||||
|
executablesComboBox.getItems().setAll(coreService.getExecutables());
|
||||||
|
executablesComboBox.getSelectionModel().selectFirst();
|
||||||
|
|
||||||
|
dependenciesComboBox.getItems().setAll(coreService.getDependencies());
|
||||||
|
dependenciesComboBox.getSelectionModel().selectFirst();
|
||||||
|
|
||||||
|
validationModeTextField.setText(coreService.getValidationMode());
|
||||||
|
validationTimerTextField.setText(coreService.getValidationTimer());
|
||||||
|
|
||||||
|
filesComboBox.getItems().setAll(coreService.getConfigs());
|
||||||
|
filesComboBox.getSelectionModel().selectFirst();
|
||||||
|
|
||||||
|
startupTextArea.setText(String.join("\n", coreService.getStartup()));
|
||||||
|
validateTextArea.setText(String.join("\n", coreService.getValidate()));
|
||||||
|
shutdownTextArea.setText(String.join("\n", coreService.getShutdown()));
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.error("error getting service data", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
}
|
177
corefx/src/main/java/com/core/ui/dialogs/SessionsDialog.java
Normal file
177
corefx/src/main/java/com/core/ui/dialogs/SessionsDialog.java
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
package com.core.ui.dialogs;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.data.SessionOverview;
|
||||||
|
import com.core.data.SessionState;
|
||||||
|
import com.core.ui.Toast;
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import javafx.concurrent.Task;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.TableColumn;
|
||||||
|
import javafx.scene.control.TableView;
|
||||||
|
import javafx.scene.control.cell.PropertyValueFactory;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class SessionsDialog extends StageDialog {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
@FXML private TableView<SessionRow> sessionsTable;
|
||||||
|
@FXML private TableColumn<SessionRow, Integer> sessionIdColumn;
|
||||||
|
@FXML private TableColumn<SessionRow, String> stateColumn;
|
||||||
|
@FXML private TableColumn<SessionRow, Integer> nodeCountColumn;
|
||||||
|
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
|
||||||
|
|
||||||
|
public SessionsDialog(Controller controller) {
|
||||||
|
super(controller, "/fxml/sessions_dialog.fxml");
|
||||||
|
setTitle("Sessions");
|
||||||
|
|
||||||
|
// add dialog buttons
|
||||||
|
JFXButton createButton = createButton("New");
|
||||||
|
createButton.setOnAction(event -> {
|
||||||
|
logger.info("creating new session");
|
||||||
|
executorService.submit(new CreateSessionTask());
|
||||||
|
});
|
||||||
|
|
||||||
|
JFXButton deleteButton = createButton("Delete");
|
||||||
|
deleteButton.setDisable(true);
|
||||||
|
deleteButton.setOnAction(event -> {
|
||||||
|
SessionRow row = sessionsTable.getSelectionModel().getSelectedItem();
|
||||||
|
Integer sessionId = row.getId();
|
||||||
|
logger.info("deleting session: {}", sessionId);
|
||||||
|
executorService.submit(new DeleteSessionTask(row, sessionId));
|
||||||
|
});
|
||||||
|
|
||||||
|
JFXButton joinButton = createButton("Join");
|
||||||
|
joinButton.setDisable(true);
|
||||||
|
joinButton.setOnAction(event -> {
|
||||||
|
SessionRow row = sessionsTable.getSelectionModel().getSelectedItem();
|
||||||
|
Integer sessionId = row.getId();
|
||||||
|
logger.info("joining session: {}", sessionId);
|
||||||
|
executorService.submit(new JoinSessionTask(sessionId));
|
||||||
|
});
|
||||||
|
|
||||||
|
addCancelButton();
|
||||||
|
|
||||||
|
// update table cell factories
|
||||||
|
sessionIdColumn.setCellValueFactory(new PropertyValueFactory<>("id"));
|
||||||
|
stateColumn.setCellValueFactory(new PropertyValueFactory<>("state"));
|
||||||
|
nodeCountColumn.setCellValueFactory(new PropertyValueFactory<>("nodes"));
|
||||||
|
|
||||||
|
// handle table row selection
|
||||||
|
sessionsTable.getSelectionModel().selectedItemProperty().addListener((ov, prev, current) -> {
|
||||||
|
if (current != null) {
|
||||||
|
boolean isCurrentSession = current.getId().equals(controller.getCoreClient().currentSession());
|
||||||
|
deleteButton.setDisable(isCurrentSession);
|
||||||
|
joinButton.setDisable(isCurrentSession);
|
||||||
|
} else {
|
||||||
|
deleteButton.setDisable(true);
|
||||||
|
joinButton.setDisable(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CreateSessionTask extends Task<Integer> {
|
||||||
|
@Override
|
||||||
|
protected Integer call() throws Exception {
|
||||||
|
SessionOverview sessionOverview = getCoreClient().createSession();
|
||||||
|
Integer sessionId = sessionOverview.getId();
|
||||||
|
getController().joinSession(sessionId);
|
||||||
|
return sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void succeeded() {
|
||||||
|
Toast.success(String.format("Created Session %s", getValue()));
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void failed() {
|
||||||
|
Toast.error("Error creating new session", new RuntimeException(getException()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class JoinSessionTask extends Task<Void> {
|
||||||
|
private Integer sessionId;
|
||||||
|
|
||||||
|
JoinSessionTask(Integer sessionId) {
|
||||||
|
this.sessionId = sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Void call() throws Exception {
|
||||||
|
getController().joinSession(sessionId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void succeeded() {
|
||||||
|
Toast.info(String.format("Joined Session %s", sessionId));
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void failed() {
|
||||||
|
Toast.error(String.format("Error joining session: %s", sessionId), new RuntimeException(getException()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DeleteSessionTask extends Task<Boolean> {
|
||||||
|
private SessionRow row;
|
||||||
|
private Integer sessionId;
|
||||||
|
|
||||||
|
DeleteSessionTask(SessionRow row, Integer sessionId) {
|
||||||
|
this.row = row;
|
||||||
|
this.sessionId = sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Boolean call() throws Exception {
|
||||||
|
return getCoreClient().deleteSession(sessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void succeeded() {
|
||||||
|
if (getValue()) {
|
||||||
|
sessionsTable.getItems().remove(row);
|
||||||
|
sessionsTable.getSelectionModel().clearSelection();
|
||||||
|
Toast.info(String.format("Deleted Session %s", sessionId));
|
||||||
|
} else {
|
||||||
|
Toast.error(String.format("Failure to delete session %s", sessionId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void failed() {
|
||||||
|
Toast.error(String.format("Error deleting session: %s", sessionId), new RuntimeException(getException()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
protected class SessionRow {
|
||||||
|
private Integer id;
|
||||||
|
private String state;
|
||||||
|
private Integer nodes;
|
||||||
|
|
||||||
|
SessionRow(SessionOverview sessionOverview) {
|
||||||
|
id = sessionOverview.getId();
|
||||||
|
state = SessionState.get(sessionOverview.getState()).name();
|
||||||
|
nodes = sessionOverview.getNodes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showDialog() throws IOException {
|
||||||
|
List<SessionOverview> sessions = getCoreClient().getSessions();
|
||||||
|
List<SessionRow> rows = sessions.stream().map(SessionRow::new).collect(Collectors.toList());
|
||||||
|
sessionsTable.getSelectionModel().clearSelection();
|
||||||
|
sessionsTable.getItems().setAll(rows);
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
package com.core.ui.dialogs;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.data.SessionOverview;
|
||||||
|
import com.core.data.SessionState;
|
||||||
|
import com.core.ui.Toast;
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.TableColumn;
|
||||||
|
import javafx.scene.control.TableView;
|
||||||
|
import javafx.scene.control.cell.PropertyValueFactory;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class SessionsFoenixDialog extends CoreFoenixDialog {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
@FXML private TableView<SessionRow> sessionsTable;
|
||||||
|
@FXML private TableColumn<SessionRow, Integer> sessionIdColumn;
|
||||||
|
@FXML private TableColumn<SessionRow, String> stateColumn;
|
||||||
|
@FXML private TableColumn<SessionRow, Integer> nodeCountColumn;
|
||||||
|
|
||||||
|
public SessionsFoenixDialog(Controller controller) {
|
||||||
|
super(controller, "/fxml/sessions_dialog.fxml");
|
||||||
|
getHeading().setText("Sessions");
|
||||||
|
JFXButton joinButton = createButton("Join");
|
||||||
|
joinButton.setOnAction(event -> {
|
||||||
|
SessionRow row = sessionsTable.getSelectionModel().getSelectedItem();
|
||||||
|
logger.info("selected session: {}", row);
|
||||||
|
try {
|
||||||
|
getController().joinSession(row.getId());
|
||||||
|
Toast.info(String.format("Joined Session %s", row.getId()));
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.error("error joining session: {}", row.getId());
|
||||||
|
}
|
||||||
|
getDialog().close();
|
||||||
|
});
|
||||||
|
getDialogLayout().setActions(joinButton);
|
||||||
|
|
||||||
|
sessionIdColumn.setCellValueFactory(new PropertyValueFactory<>("id"));
|
||||||
|
stateColumn.setCellValueFactory(new PropertyValueFactory<>("state"));
|
||||||
|
nodeCountColumn.setCellValueFactory(new PropertyValueFactory<>("nodes"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
protected class SessionRow {
|
||||||
|
private Integer id;
|
||||||
|
private String state;
|
||||||
|
private Integer nodes;
|
||||||
|
|
||||||
|
public SessionRow(SessionOverview sessionOverview) {
|
||||||
|
id = sessionOverview.getId();
|
||||||
|
state = SessionState.get(sessionOverview.getState()).name();
|
||||||
|
nodes = sessionOverview.getNodes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showDialog() throws IOException {
|
||||||
|
sessionsTable.getItems().clear();
|
||||||
|
List<SessionOverview> sessions = getCoreClient().getSessions();
|
||||||
|
sessionsTable.getItems().addAll(sessions.stream()
|
||||||
|
.map(SessionRow::new)
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
getDialog().show(getController().getStackPane());
|
||||||
|
}
|
||||||
|
}
|
132
corefx/src/main/java/com/core/ui/dialogs/StageDialog.java
Normal file
132
corefx/src/main/java/com/core/ui/dialogs/StageDialog.java
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
package com.core.ui.dialogs;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.client.ICoreClient;
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import com.jfoenix.controls.JFXDecorator;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.geometry.HPos;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.geometry.VPos;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.Parent;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.input.KeyCode;
|
||||||
|
import javafx.scene.layout.*;
|
||||||
|
import javafx.stage.Modality;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import javafx.stage.StageStyle;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class StageDialog {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private final Controller controller;
|
||||||
|
private final Stage stage = new Stage(StageStyle.DECORATED);
|
||||||
|
private final Scene scene;
|
||||||
|
private final GridPane gridPane = new GridPane();
|
||||||
|
private final HBox buttonBar = new HBox();
|
||||||
|
|
||||||
|
public StageDialog(Controller controller, String fxmlPath) {
|
||||||
|
this(controller, fxmlPath, Modality.APPLICATION_MODAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StageDialog(Controller controller, String fxmlPath, Modality modality) {
|
||||||
|
this.controller = controller;
|
||||||
|
|
||||||
|
JFXDecorator decorator = new JFXDecorator(stage, gridPane);
|
||||||
|
decorator.setCustomMaximize(true);
|
||||||
|
Image coreIcon = new Image(getClass().getResourceAsStream("/core-icon.png"));
|
||||||
|
decorator.setGraphic(new ImageView(coreIcon));
|
||||||
|
|
||||||
|
scene = new Scene(decorator);
|
||||||
|
stage.setScene(scene);
|
||||||
|
|
||||||
|
stage.setWidth(800);
|
||||||
|
stage.setHeight(600);
|
||||||
|
scene.setOnKeyPressed(event -> {
|
||||||
|
if (KeyCode.ESCAPE == event.getCode()) {
|
||||||
|
stage.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
gridPane.setHgap(10);
|
||||||
|
gridPane.setVgap(10);
|
||||||
|
gridPane.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
gridPane.setMaxHeight(Double.MAX_VALUE);
|
||||||
|
gridPane.setPadding(new Insets(10));
|
||||||
|
|
||||||
|
gridPane.getColumnConstraints().add(new ColumnConstraints(10, Region.USE_COMPUTED_SIZE,
|
||||||
|
Region.USE_COMPUTED_SIZE, Priority.ALWAYS, HPos.CENTER, true));
|
||||||
|
gridPane.getRowConstraints().add(new RowConstraints(10, Region.USE_COMPUTED_SIZE,
|
||||||
|
Region.USE_COMPUTED_SIZE, Priority.ALWAYS, VPos.CENTER, true));
|
||||||
|
gridPane.getRowConstraints().add(new RowConstraints(30, 30,
|
||||||
|
Region.USE_COMPUTED_SIZE, Priority.NEVER, VPos.CENTER, true));
|
||||||
|
|
||||||
|
buttonBar.setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
buttonBar.setSpacing(10);
|
||||||
|
|
||||||
|
stage.initModality(modality);
|
||||||
|
|
||||||
|
FXMLLoader loader = new FXMLLoader(getClass().getResource(fxmlPath));
|
||||||
|
loader.setController(this);
|
||||||
|
try {
|
||||||
|
Parent parent = loader.load();
|
||||||
|
setContent(parent);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.error("error loading fxml: {}", fxmlPath, ex);
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
gridPane.addRow(1, buttonBar);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
stage.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICoreClient getCoreClient() {
|
||||||
|
return controller.getCoreClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContent(Node node) {
|
||||||
|
gridPane.addRow(0, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitle(String title) {
|
||||||
|
stage.setTitle(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOwner(Stage window) {
|
||||||
|
stage.initOwner(window);
|
||||||
|
scene.getStylesheets().addAll(window.getScene().getStylesheets());
|
||||||
|
}
|
||||||
|
|
||||||
|
public JFXButton createButton(String label) {
|
||||||
|
JFXButton button = new JFXButton(label);
|
||||||
|
button.getStyleClass().add("core-button");
|
||||||
|
buttonBar.getChildren().add(button);
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addCancelButton() {
|
||||||
|
JFXButton button = createButton("Cancel");
|
||||||
|
button.setOnAction(event -> close());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show() {
|
||||||
|
if (buttonBar.getChildren().isEmpty() && gridPane.getChildren().contains(buttonBar)) {
|
||||||
|
gridPane.getChildren().remove(1);
|
||||||
|
gridPane.getRowConstraints().remove(1);
|
||||||
|
gridPane.setVgap(0);
|
||||||
|
}
|
||||||
|
stage.showAndWait();
|
||||||
|
}
|
||||||
|
}
|
72
corefx/src/main/java/com/core/ui/dialogs/TerminalDialog.java
Normal file
72
corefx/src/main/java/com/core/ui/dialogs/TerminalDialog.java
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
package com.core.ui.dialogs;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.data.CoreNode;
|
||||||
|
import com.core.ui.Toast;
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import com.jfoenix.controls.JFXTextArea;
|
||||||
|
import com.jfoenix.controls.JFXTextField;
|
||||||
|
import javafx.concurrent.Task;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.stage.Modality;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class TerminalDialog extends StageDialog {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private String address;
|
||||||
|
private int port;
|
||||||
|
private JFXButton saveButton;
|
||||||
|
private @FXML JFXTextArea outputTextArea;
|
||||||
|
private @FXML JFXTextField commandTextField;
|
||||||
|
private CoreNode node;
|
||||||
|
|
||||||
|
public TerminalDialog(Controller controller) {
|
||||||
|
super(controller, "/fxml/terminal_dialog.fxml", Modality.NONE);
|
||||||
|
commandTextField.setOnAction(event -> {
|
||||||
|
String command = commandTextField.getText();
|
||||||
|
addOutput(String.format("$> %s", command));
|
||||||
|
new Thread(new CommandTask(command)).start();
|
||||||
|
commandTextField.clear();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private class CommandTask extends Task<String> {
|
||||||
|
private String command;
|
||||||
|
|
||||||
|
CommandTask(String command) {
|
||||||
|
this.command = command;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String call() throws Exception {
|
||||||
|
return getCoreClient().nodeCommand(node, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void succeeded() {
|
||||||
|
addOutput(getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void failed() {
|
||||||
|
Toast.error("Failed sending terminal command", new RuntimeException(getException()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addOutput(String output) {
|
||||||
|
outputTextArea.appendText(String.format("%s%n", output));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showDialog(CoreNode node) {
|
||||||
|
this.node = node;
|
||||||
|
setTitle(String.format("%s Pseudo Terminal", node.getName()));
|
||||||
|
outputTextArea.setText("");
|
||||||
|
commandTextField.setText("");
|
||||||
|
commandTextField.requestFocus();
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.core.ui.textfields;
|
||||||
|
|
||||||
|
import javafx.scene.control.TextFormatter;
|
||||||
|
|
||||||
|
import java.util.function.UnaryOperator;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class DoubleFilter implements UnaryOperator<TextFormatter.Change> {
|
||||||
|
private static final Pattern DIGIT_PATTERN = Pattern.compile("\\d*\\.?\\d*");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TextFormatter.Change apply(TextFormatter.Change change) {
|
||||||
|
return DIGIT_PATTERN.matcher(change.getText()).matches() ? change : null;
|
||||||
|
}
|
||||||
|
}
|
122
corefx/src/main/java/com/core/utils/ConfigUtils.java
Normal file
122
corefx/src/main/java/com/core/utils/ConfigUtils.java
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
package com.core.utils;
|
||||||
|
|
||||||
|
import com.core.data.NodeType;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public final class ConfigUtils {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private static final Path HOME = Paths.get(System.getProperty("user.home"), ".core");
|
||||||
|
private static final String CONFIG_FILE_NAME = "config.json";
|
||||||
|
private static final String DEFAULT_CONFIG = "/" + CONFIG_FILE_NAME;
|
||||||
|
private static final Path CONFIG_FILE = Paths.get(HOME.toString(), CONFIG_FILE_NAME);
|
||||||
|
private static final Path XML_DIR = Paths.get(HOME.toString(), "xml");
|
||||||
|
private static final Path MOBILITY_DIR = Paths.get(HOME.toString(), "mobility");
|
||||||
|
private static final Path ICON_DIR = Paths.get(HOME.toString(), "icons");
|
||||||
|
|
||||||
|
|
||||||
|
private ConfigUtils() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void save(Configuration configuration) throws IOException {
|
||||||
|
String fileData = JsonUtils.toPrettyString(configuration);
|
||||||
|
try (PrintWriter out = new PrintWriter(CONFIG_FILE.toFile())) {
|
||||||
|
out.println(fileData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Configuration readConfig() throws IOException {
|
||||||
|
logger.info("reading config file: {}", CONFIG_FILE);
|
||||||
|
return JsonUtils.read(new FileInputStream(CONFIG_FILE.toFile()), Configuration.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<NodeTypeConfig> createDefaults() throws IOException {
|
||||||
|
return Arrays.asList(
|
||||||
|
createDefault("host", "Host", "/icons/host-100.png", new TreeSet<>(Arrays.asList(
|
||||||
|
"DefaultRoute", "SSH"
|
||||||
|
))),
|
||||||
|
createDefault("PC", "PC", "/icons/pc-100.png",
|
||||||
|
new TreeSet<>(Collections.singletonList("DefaultRoute"))),
|
||||||
|
createDefault("mdr", "MDR", "/icons/router-100.png", new TreeSet<>(Arrays.asList(
|
||||||
|
"zebra", "OSPFv3MDR", "IPForward"
|
||||||
|
)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static NodeTypeConfig createDefault(String model, String display, String icon,
|
||||||
|
Set<String> services) throws IOException {
|
||||||
|
String fileName = Paths.get(icon).getFileName().toString();
|
||||||
|
Path iconPath = Paths.get(ICON_DIR.toString(), fileName);
|
||||||
|
Files.copy(ConfigUtils.class.getResourceAsStream(icon), iconPath);
|
||||||
|
return new NodeTypeConfig(model, display, iconPath.toUri().toString(), services);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkDirectory(Path path) throws IOException {
|
||||||
|
if (!path.toFile().exists()) {
|
||||||
|
Files.createDirectory(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void checkHomeDirectory() throws IOException {
|
||||||
|
logger.info("checking core home directory");
|
||||||
|
checkDirectory(HOME);
|
||||||
|
checkDirectory(XML_DIR);
|
||||||
|
checkDirectory(MOBILITY_DIR);
|
||||||
|
checkDirectory(ICON_DIR);
|
||||||
|
if (!CONFIG_FILE.toFile().exists()) {
|
||||||
|
createDefaultConfigFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void createDefaultConfigFile() throws IOException {
|
||||||
|
logger.info("creating default configuration");
|
||||||
|
Files.copy(ConfigUtils.class.getResourceAsStream(DEFAULT_CONFIG), CONFIG_FILE);
|
||||||
|
Configuration configuration = readConfig();
|
||||||
|
configuration.setXmlPath(XML_DIR.toString());
|
||||||
|
configuration.setMobilityPath(MOBILITY_DIR.toString());
|
||||||
|
configuration.setIconPath(ICON_DIR.toString());
|
||||||
|
configuration.setNodeTypeConfigs(createDefaults());
|
||||||
|
configuration.setNodeLabelColor(Color.WHITE.toString());
|
||||||
|
configuration.setNodeLabelBackgroundColor(Color.BLACK.toString());
|
||||||
|
save(configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Configuration load() {
|
||||||
|
try {
|
||||||
|
Configuration configuration = readConfig();
|
||||||
|
|
||||||
|
// initialize node types
|
||||||
|
for (NodeTypeConfig nodeTypeConfig : configuration.getNodeTypeConfigs()) {
|
||||||
|
NodeType nodeType = new NodeType(
|
||||||
|
NodeType.DEFAULT,
|
||||||
|
nodeTypeConfig.getModel(),
|
||||||
|
nodeTypeConfig.getDisplay(),
|
||||||
|
nodeTypeConfig.getIcon()
|
||||||
|
);
|
||||||
|
nodeType.getServices().addAll(nodeTypeConfig.getServices());
|
||||||
|
NodeType.add(nodeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// override configuration from command line
|
||||||
|
String coreAddress = System.getProperty("coreAddress");
|
||||||
|
if (coreAddress != null) {
|
||||||
|
configuration.setCoreAddress(coreAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
return configuration;
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.error("error reading config file", ex);
|
||||||
|
throw new RuntimeException("configuration file did not exist", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
corefx/src/main/java/com/core/utils/Configuration.java
Normal file
23
corefx/src/main/java/com/core/utils/Configuration.java
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package com.core.utils;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class Configuration {
|
||||||
|
private String coreAddress;
|
||||||
|
private int corePort;
|
||||||
|
private String xmlPath;
|
||||||
|
private String mobilityPath;
|
||||||
|
private String iconPath;
|
||||||
|
private String shellCommand;
|
||||||
|
private List<NodeTypeConfig> nodeTypeConfigs = new ArrayList<>();
|
||||||
|
private String nodeLabelColor;
|
||||||
|
private String nodeLabelBackgroundColor;
|
||||||
|
private Double throughputLimit;
|
||||||
|
private Integer throughputWidth;
|
||||||
|
}
|
22
corefx/src/main/java/com/core/utils/FxmlUtils.java
Normal file
22
corefx/src/main/java/com/core/utils/FxmlUtils.java
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package com.core.utils;
|
||||||
|
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public final class FxmlUtils {
|
||||||
|
private FxmlUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void loadRootController(Object obj, String fxmlPath) {
|
||||||
|
FXMLLoader loader = new FXMLLoader(FxmlUtils.class.getResource(fxmlPath));
|
||||||
|
loader.setRoot(obj);
|
||||||
|
loader.setController(obj);
|
||||||
|
|
||||||
|
try {
|
||||||
|
loader.load();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
65
corefx/src/main/java/com/core/utils/IconUtils.java
Normal file
65
corefx/src/main/java/com/core/utils/IconUtils.java
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
package com.core.utils;
|
||||||
|
|
||||||
|
import com.jfoenix.svg.SVGGlyph;
|
||||||
|
import com.jfoenix.svg.SVGGlyphLoader;
|
||||||
|
import edu.uci.ics.jung.visualization.LayeredIcon;
|
||||||
|
import javafx.scene.paint.Paint;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public final class IconUtils {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
public static final int ICON_SIZE = 75;
|
||||||
|
private static final Map<String, ImageIcon> ICON_MAP = new HashMap<>();
|
||||||
|
|
||||||
|
private IconUtils() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LayeredIcon getExternalLayeredIcon(String iconPath) {
|
||||||
|
ImageIcon imageIcon = ICON_MAP.computeIfAbsent(iconPath, key -> {
|
||||||
|
try {
|
||||||
|
return new ImageIcon(Paths.get(new URI(iconPath)).toString());
|
||||||
|
} catch (URISyntaxException ex) {
|
||||||
|
logger.error("error loading icon: {}", iconPath);
|
||||||
|
throw new IllegalArgumentException("invalid icon uri: " + iconPath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Image image = imageIcon.getImage().getScaledInstance(ICON_SIZE, ICON_SIZE, Image.SCALE_DEFAULT);
|
||||||
|
return new LayeredIcon(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LayeredIcon getLayeredIcon(String iconPath) {
|
||||||
|
ImageIcon imageIcon = ICON_MAP.computeIfAbsent(iconPath, key ->
|
||||||
|
new ImageIcon(IconUtils.class.getResource(iconPath)));
|
||||||
|
Image image = imageIcon.getImage().getScaledInstance(ICON_SIZE, ICON_SIZE, Image.SCALE_DEFAULT);
|
||||||
|
return new LayeredIcon(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SVGGlyph get(String name) {
|
||||||
|
SVGGlyph svg = null;
|
||||||
|
try {
|
||||||
|
svg = SVGGlyphLoader.getIcoMoonGlyph("icomoon.svg." + name);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
logger.error("error loading icon: {}", name, ex);
|
||||||
|
}
|
||||||
|
return svg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SVGGlyph get(String name, int size, String paint) {
|
||||||
|
SVGGlyph svg = get(name);
|
||||||
|
if (svg != null) {
|
||||||
|
svg.setSize(size);
|
||||||
|
svg.setFill(Paint.valueOf(paint));
|
||||||
|
}
|
||||||
|
return svg;
|
||||||
|
}
|
||||||
|
}
|
53
corefx/src/main/java/com/core/utils/JsonUtils.java
Normal file
53
corefx/src/main/java/com/core/utils/JsonUtils.java
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package com.core.utils;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
public final class JsonUtils {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private static ObjectMapper mapper = new ObjectMapper();
|
||||||
|
|
||||||
|
private JsonUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ObjectMapper getMapper() {
|
||||||
|
return mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T read(byte[] data, Class<T> clazz) throws IOException {
|
||||||
|
return mapper.readValue(data, clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T read(String data, Class<T> clazz) throws IOException {
|
||||||
|
return mapper.readValue(data, clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T read(InputStream input, Class<T> clazz) throws IOException {
|
||||||
|
return mapper.readValue(input, clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void logPrettyJson(Object obj) {
|
||||||
|
try {
|
||||||
|
logger.debug(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj));
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
logger.error("error making pretty json", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toPrettyString(Object obj) throws JsonProcessingException {
|
||||||
|
return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toString(Object obj) throws JsonProcessingException {
|
||||||
|
return mapper.writeValueAsString(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] toBytes(Object obj) throws JsonProcessingException {
|
||||||
|
return mapper.writeValueAsBytes(obj);
|
||||||
|
}
|
||||||
|
}
|
20
corefx/src/main/java/com/core/utils/NodeTypeConfig.java
Normal file
20
corefx/src/main/java/com/core/utils/NodeTypeConfig.java
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package com.core.utils;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class NodeTypeConfig {
|
||||||
|
@EqualsAndHashCode.Include
|
||||||
|
private String model;
|
||||||
|
private String display;
|
||||||
|
private String icon;
|
||||||
|
private Set<String> services;
|
||||||
|
}
|
174
corefx/src/main/java/com/core/utils/WebUtils.java
Normal file
174
corefx/src/main/java/com/core/utils/WebUtils.java
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
package com.core.utils;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import okhttp3.*;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public final class WebUtils {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private static final OkHttpClient client = new OkHttpClient();
|
||||||
|
private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
|
||||||
|
|
||||||
|
private WebUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T getJson(String url, Class<T> clazz) throws IOException {
|
||||||
|
return getJson(url, clazz, Collections.emptyMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T getJson(String url, Class<T> clazz, Map<String, String> args) throws IOException {
|
||||||
|
logger.debug("get json: {}", url);
|
||||||
|
HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
|
||||||
|
args.forEach(urlBuilder::addQueryParameter);
|
||||||
|
HttpUrl httpUrl = urlBuilder.build();
|
||||||
|
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(httpUrl)
|
||||||
|
.build();
|
||||||
|
String response = readResponse(request);
|
||||||
|
return JsonUtils.read(response, clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void getFile(String url, File file) throws IOException {
|
||||||
|
logger.debug("get file: {}", url);
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.build();
|
||||||
|
try (Response response = client.newCall(request).execute()) {
|
||||||
|
InputStream input = response.body().byteStream();
|
||||||
|
try (OutputStream output = new FileOutputStream(file)) {
|
||||||
|
int count;
|
||||||
|
byte[] data = new byte[1024];
|
||||||
|
while ((count = input.read(data)) != -1) {
|
||||||
|
output.write(data, 0, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean postFile(String url, File file) throws IOException {
|
||||||
|
MediaType mediaType = MediaType.parse("File/*");
|
||||||
|
RequestBody requestBody = new MultipartBody.Builder()
|
||||||
|
.setType(MultipartBody.FORM)
|
||||||
|
.addFormDataPart("file", file.getName(), RequestBody.create(mediaType, file))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.post(requestBody)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try (Response response = client.newCall(request).execute()) {
|
||||||
|
return response.isSuccessful();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T postFile(String url, File file, Class<T> clazz) throws IOException {
|
||||||
|
MediaType mediaType = MediaType.parse("File/*");
|
||||||
|
RequestBody requestBody = new MultipartBody.Builder()
|
||||||
|
.setType(MultipartBody.FORM)
|
||||||
|
.addFormDataPart("file", file.getName(), RequestBody.create(mediaType, file))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.post(requestBody)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
String response = readResponse(request);
|
||||||
|
return JsonUtils.read(response, clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T getJson(String url, TypeReference<T> reference) throws IOException {
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.build();
|
||||||
|
String response = readResponse(request);
|
||||||
|
return JsonUtils.getMapper().readValue(response, reference);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String readResponse(Request request) throws IOException {
|
||||||
|
try (Response response = client.newCall(request).execute()) {
|
||||||
|
ResponseBody body = response.body();
|
||||||
|
if (body == null) {
|
||||||
|
throw new IOException("failed to received body");
|
||||||
|
} else {
|
||||||
|
return body.string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean postJson(String url, Object json) throws IOException {
|
||||||
|
logger.debug("post json: {} - {}", url, json);
|
||||||
|
RequestBody body = RequestBody.create(JSON, JsonUtils.toString(json));
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.post(body)
|
||||||
|
.build();
|
||||||
|
try (Response response = client.newCall(request).execute()) {
|
||||||
|
return response.isSuccessful();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean putJson(String url) throws IOException {
|
||||||
|
logger.debug("put json: {}", url);
|
||||||
|
RequestBody body = new FormBody.Builder().build();
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.put(body)
|
||||||
|
.build();
|
||||||
|
try (Response response = client.newCall(request).execute()) {
|
||||||
|
return response.isSuccessful();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean putJson(String url, Object json) throws IOException {
|
||||||
|
logger.debug("put json: {} - {}", url, json);
|
||||||
|
RequestBody body = RequestBody.create(JSON, JsonUtils.toString(json));
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.put(body)
|
||||||
|
.build();
|
||||||
|
try (Response response = client.newCall(request).execute()) {
|
||||||
|
return response.isSuccessful();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T putJson(String url, Object json, Class<T> clazz) throws IOException {
|
||||||
|
logger.debug("put json: {} - {}", url, json);
|
||||||
|
RequestBody body = RequestBody.create(JSON, JsonUtils.toString(json));
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.put(body)
|
||||||
|
.build();
|
||||||
|
String response = readResponse(request);
|
||||||
|
return JsonUtils.read(response, clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T post(String url, Class<T> clazz) throws IOException {
|
||||||
|
logger.debug("post: {}", url);
|
||||||
|
RequestBody body = new FormBody.Builder().build();
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.post(body)
|
||||||
|
.build();
|
||||||
|
String response = readResponse(request);
|
||||||
|
return JsonUtils.read(response, clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean delete(String url) throws IOException {
|
||||||
|
logger.debug("delete: {}", url);
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.delete()
|
||||||
|
.build();
|
||||||
|
try (Response response = client.newCall(request).execute()) {
|
||||||
|
return response.isSuccessful();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue