changes to support mobility from rest and configuring mobility from a wlan context menu
This commit is contained in:
parent
dd9ad5644d
commit
f2f83f247d
12 changed files with 379 additions and 15 deletions
|
@ -71,6 +71,7 @@ public class Controller implements Initializable {
|
||||||
private NodeWlanDialog nodeWlanDialog = new NodeWlanDialog(this);
|
private NodeWlanDialog nodeWlanDialog = new NodeWlanDialog(this);
|
||||||
private ConfigDialog configDialog = new ConfigDialog(this);
|
private ConfigDialog configDialog = new ConfigDialog(this);
|
||||||
private HooksDialog hooksDialog = new HooksDialog(this);
|
private HooksDialog hooksDialog = new HooksDialog(this);
|
||||||
|
private MobilityDialog mobilityDialog = new MobilityDialog(this);
|
||||||
|
|
||||||
public Controller() {
|
public Controller() {
|
||||||
// load configuration
|
// load configuration
|
||||||
|
@ -108,6 +109,7 @@ public class Controller implements Initializable {
|
||||||
nodeWlanDialog.setOwner(window);
|
nodeWlanDialog.setOwner(window);
|
||||||
nodeEmaneDialog.setOwner(window);
|
nodeEmaneDialog.setOwner(window);
|
||||||
configDialog.setOwner(window);
|
configDialog.setOwner(window);
|
||||||
|
mobilityDialog.setOwner(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
|
|
|
@ -39,9 +39,10 @@ public class CoreClient {
|
||||||
logger.info("joining core session({}) state({}): {}", sessionId, sessionState, session);
|
logger.info("joining core session({}) state({}): {}", sessionId, sessionState, session);
|
||||||
for (CoreNode node : session.getNodes()) {
|
for (CoreNode node : session.getNodes()) {
|
||||||
if (node.getModel() == null) {
|
if (node.getModel() == null) {
|
||||||
|
logger.info("skipping joined session node: {}", node.getName());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeType nodeType = NodeType.getNodeType(node.getNodeTypeKey());
|
NodeType nodeType = NodeType.getNodeType(node.getNodeTypeKey());
|
||||||
node.setIcon(nodeType.getIcon());
|
node.setIcon(nodeType.getIcon());
|
||||||
networkGraph.addNode(node);
|
networkGraph.addNode(node);
|
||||||
|
@ -220,4 +221,16 @@ public class CoreClient {
|
||||||
public String getTerminalCommand(CoreNode node) throws IOException {
|
public String getTerminalCommand(CoreNode node) throws IOException {
|
||||||
return coreApi.getTerminalCommand(sessionId, node);
|
return coreApi.getTerminalCommand(sessionId, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean setMobilityConfig(CoreNode node, MobilityConfig config) throws IOException {
|
||||||
|
return coreApi.setMobilityConfig(sessionId, node, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MobilityConfig getMobilityConfig(CoreNode node) throws IOException {
|
||||||
|
return coreApi.getMobilityConfig(sessionId, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean mobilityAction(CoreNode node, String action) throws IOException {
|
||||||
|
return coreApi.mobilityAction(sessionId, node, action);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
20
corefx/src/main/java/com/core/data/MobilityConfig.java
Normal file
20
corefx/src/main/java/com/core/data/MobilityConfig.java
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package com.core.data;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class MobilityConfig {
|
||||||
|
private String file;
|
||||||
|
@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;
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
package com.core.data;
|
package com.core.data;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -8,6 +10,7 @@ import java.util.Map;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class NodeType {
|
public class NodeType {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
public static final int DEFAULT = 0;
|
public static final int DEFAULT = 0;
|
||||||
public static final int SWITCH = 4;
|
public static final int SWITCH = 4;
|
||||||
public static final int HUB = 5;
|
public static final int HUB = 5;
|
||||||
|
@ -36,7 +39,7 @@ public class NodeType {
|
||||||
addNodeType(new NodeType(DEFAULT, "mdr", "MDR", "/icons/router-100.png"));
|
addNodeType(new NodeType(DEFAULT, "mdr", "MDR", "/icons/router-100.png"));
|
||||||
addNodeType(new NodeType(SWITCH, "Switch", "/icons/switch-100.png"));
|
addNodeType(new NodeType(SWITCH, "Switch", "/icons/switch-100.png"));
|
||||||
addNodeType(new NodeType(HUB, "Hub", "/icons/hub-100.png"));
|
addNodeType(new NodeType(HUB, "Hub", "/icons/hub-100.png"));
|
||||||
addNodeType(new NodeType(WLAN, "WLAN", "/icons/wlan-100.png"));
|
addNodeType(new NodeType(WLAN, "wlan", "WLAN", "/icons/wlan-100.png"));
|
||||||
addNodeType(new NodeType(EMANE, "EMANE", "/icons/emane-100.png"));
|
addNodeType(new NodeType(EMANE, "EMANE", "/icons/emane-100.png"));
|
||||||
|
|
||||||
DISPLAY_MAP.put(HUB, "Hub");
|
DISPLAY_MAP.put(HUB, "Hub");
|
||||||
|
|
|
@ -82,6 +82,8 @@ public class CorePopupGraphMousePlugin<V, E> extends EditingPopupGraphMousePlugi
|
||||||
case NodeType.WLAN:
|
case NodeType.WLAN:
|
||||||
menuItems.add(createMenuItem("WLAN Settings",
|
menuItems.add(createMenuItem("WLAN Settings",
|
||||||
event -> controller.getNodeWlanDialog().showDialog(node)));
|
event -> controller.getNodeWlanDialog().showDialog(node)));
|
||||||
|
menuItems.add(createMenuItem("Mobility",
|
||||||
|
event -> controller.getMobilityDialog().showDialog(node)));
|
||||||
menuItems.add(createMenuItem("Link MDRs",
|
menuItems.add(createMenuItem("Link MDRs",
|
||||||
event -> networkGraph.linkMdrs(node)));
|
event -> networkGraph.linkMdrs(node)));
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -271,6 +271,19 @@ public class NetworkGraph {
|
||||||
nodeMap.put(node.getId(), node);
|
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) {
|
public void removeNode(CoreNode node) {
|
||||||
try {
|
try {
|
||||||
controller.getCoreClient().deleteNode(node);
|
controller.getCoreClient().deleteNode(node);
|
||||||
|
|
|
@ -170,6 +170,22 @@ public class CoreApi {
|
||||||
return WebUtils.putJson(url, jsonData);
|
return WebUtils.putJson(url, jsonData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean setMobilityConfig(Integer session, CoreNode node, MobilityConfig config) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/nodes/%s/mobility", session, node.getId()));
|
||||||
|
String data = JsonUtils.toString(config);
|
||||||
|
return WebUtils.postJson(url, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MobilityConfig getMobilityConfig(Integer session, CoreNode node) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/nodes/%s/mobility", session, node.getId()));
|
||||||
|
return WebUtils.getJson(url, MobilityConfig.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean mobilityAction(Integer session, CoreNode node, String action) throws IOException {
|
||||||
|
String url = getUrl(String.format("sessions/%s/nodes/%s/mobility/%s", session, node.getId(), action));
|
||||||
|
return WebUtils.putJson(url, null);
|
||||||
|
}
|
||||||
|
|
||||||
public String getTerminalCommand(Integer session, CoreNode node) throws IOException {
|
public String getTerminalCommand(Integer session, CoreNode node) throws IOException {
|
||||||
String url = getUrl(String.format("sessions/%s/nodes/%s/terminal", session, node.getId()));
|
String url = getUrl(String.format("sessions/%s/nodes/%s/terminal", session, node.getId()));
|
||||||
return WebUtils.getJson(url, String.class);
|
return WebUtils.getJson(url, String.class);
|
||||||
|
|
117
corefx/src/main/java/com/core/ui/MobilityDialog.java
Normal file
117
corefx/src/main/java/com/core/ui/MobilityDialog.java
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
package com.core.ui;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.data.CoreNode;
|
||||||
|
import com.core.data.MobilityConfig;
|
||||||
|
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 org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
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.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 {
|
||||||
|
controller.getCoreClient().setMobilityConfig(node, mobilityConfig);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.error("error setting mobility configuration", ex);
|
||||||
|
Toast.error("error setting mobility configuration");
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
fileChooser.setInitialDirectory(new File(System.getProperty("user.home")));
|
||||||
|
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Mobility",
|
||||||
|
"*.mobility"));
|
||||||
|
File file = fileChooser.showOpenDialog(getController().getWindow());
|
||||||
|
if (file != null) {
|
||||||
|
logger.info("opening session xml: {}", file.getPath());
|
||||||
|
textField.setText(file.getPath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
logger.error("error getting mobility config", ex);
|
||||||
|
Toast.error("error getting mobility config");
|
||||||
|
}
|
||||||
|
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ package com.core.websocket;
|
||||||
|
|
||||||
import com.core.Controller;
|
import com.core.Controller;
|
||||||
import com.core.data.CoreEvent;
|
import com.core.data.CoreEvent;
|
||||||
|
import com.core.data.CoreNode;
|
||||||
import com.core.data.SessionState;
|
import com.core.data.SessionState;
|
||||||
import com.core.utils.JsonUtils;
|
import com.core.utils.JsonUtils;
|
||||||
import io.socket.client.IO;
|
import io.socket.client.IO;
|
||||||
|
@ -26,6 +27,17 @@ public class CoreWebSocket {
|
||||||
socket.on(Socket.EVENT_CONNECT, args -> {
|
socket.on(Socket.EVENT_CONNECT, args -> {
|
||||||
logger.info("connected to web socket");
|
logger.info("connected to web socket");
|
||||||
});
|
});
|
||||||
|
socket.on("node", args -> {
|
||||||
|
for (Object arg : args) {
|
||||||
|
try {
|
||||||
|
CoreNode node = JsonUtils.read(arg.toString(), CoreNode.class);
|
||||||
|
logger.info("core node update: {}", node);
|
||||||
|
controller.getNetworkGraph().setNodeLocation(node);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.error("error getting core node", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
socket.on("event", args -> {
|
socket.on("event", args -> {
|
||||||
for (Object arg : args) {
|
for (Object arg : args) {
|
||||||
try {
|
try {
|
||||||
|
|
96
corefx/src/main/resources/fxml/mobility_dialog.fxml
Normal file
96
corefx/src/main/resources/fxml/mobility_dialog.fxml
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import com.jfoenix.controls.JFXButton?>
|
||||||
|
<?import com.jfoenix.controls.JFXTextField?>
|
||||||
|
<?import com.jfoenix.controls.JFXToggleButton?>
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.layout.ColumnConstraints?>
|
||||||
|
<?import javafx.scene.layout.GridPane?>
|
||||||
|
<?import javafx.scene.layout.RowConstraints?>
|
||||||
|
|
||||||
|
<GridPane hgap="10.0" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" vgap="10.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1">
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="40.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="NEVER" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="NEVER" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="NEVER" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="NEVER" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="NEVER" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="NEVER" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="NEVER" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="NEVER" />
|
||||||
|
</rowConstraints>
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||||
|
</padding>
|
||||||
|
<children>
|
||||||
|
<Label text="File" />
|
||||||
|
<Label text="Refresh Rate (ms)" GridPane.rowIndex="1" />
|
||||||
|
<Label text="Auto-Start (0.0 for runtime)" GridPane.rowIndex="3" />
|
||||||
|
<Label text="Node Mapping (optional, e.g. 0:1,1:2,etc)" GridPane.rowIndex="4" />
|
||||||
|
<Label text="Start Script" GridPane.rowIndex="5" />
|
||||||
|
<Label text="Pause Script" GridPane.rowIndex="6" />
|
||||||
|
<Label text="Stop Script" GridPane.rowIndex="7" />
|
||||||
|
<JFXTextField fx:id="refreshTextField" GridPane.columnIndex="1" GridPane.rowIndex="1" />
|
||||||
|
<JFXTextField fx:id="autoStartTextField" GridPane.columnIndex="1" GridPane.rowIndex="3" />
|
||||||
|
<JFXTextField fx:id="nodeMappingTextField" GridPane.columnIndex="1" GridPane.rowIndex="4" />
|
||||||
|
<JFXToggleButton fx:id="loopToggleButton" contentDisplay="GRAPHIC_ONLY" maxWidth="1.7976931348623157E308" text="Loop?" GridPane.columnIndex="1" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="2" />
|
||||||
|
<Label text="Loop?" GridPane.rowIndex="2" />
|
||||||
|
<GridPane hgap="5.0" GridPane.columnIndex="1">
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="80.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
</rowConstraints>
|
||||||
|
<children>
|
||||||
|
<JFXTextField fx:id="fileTextField" />
|
||||||
|
<JFXButton maxWidth="1.7976931348623157E308" onAction="#onSelectAction" styleClass="core-button" text="Select" GridPane.columnIndex="1" />
|
||||||
|
</children>
|
||||||
|
</GridPane>
|
||||||
|
<GridPane hgap="5.0" GridPane.columnIndex="1" GridPane.rowIndex="6">
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="80.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
</rowConstraints>
|
||||||
|
<children>
|
||||||
|
<JFXTextField fx:id="pauseTextField" />
|
||||||
|
<JFXButton maxWidth="1.7976931348623157E308" onAction="#onSelectAction" styleClass="core-button" text="Select" GridPane.columnIndex="1" />
|
||||||
|
</children>
|
||||||
|
</GridPane>
|
||||||
|
<GridPane hgap="5.0" GridPane.columnIndex="1" GridPane.rowIndex="7">
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="80.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
</rowConstraints>
|
||||||
|
<children>
|
||||||
|
<JFXTextField fx:id="stopTextField" />
|
||||||
|
<JFXButton maxWidth="1.7976931348623157E308" onAction="#onSelectAction" styleClass="core-button" text="Select" GridPane.columnIndex="1" />
|
||||||
|
</children>
|
||||||
|
</GridPane>
|
||||||
|
<GridPane hgap="5.0" GridPane.columnIndex="1" GridPane.rowIndex="5">
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="80.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
</rowConstraints>
|
||||||
|
<children>
|
||||||
|
<JFXTextField fx:id="startTextField" />
|
||||||
|
<JFXButton maxWidth="1.7976931348623157E308" onAction="#onSelectAction" styleClass="core-button" text="Select" GridPane.columnIndex="1" />
|
||||||
|
</children>
|
||||||
|
</GridPane>
|
||||||
|
</children>
|
||||||
|
</GridPane>
|
|
@ -11,6 +11,7 @@ from flask import send_file
|
||||||
from flask_socketio import SocketIO
|
from flask_socketio import SocketIO
|
||||||
from flask_socketio import emit
|
from flask_socketio import emit
|
||||||
|
|
||||||
|
import mobility_routes
|
||||||
from core import logger
|
from core import logger
|
||||||
from core.emulator.coreemu import CoreEmu
|
from core.emulator.coreemu import CoreEmu
|
||||||
from core.emulator.emudata import InterfaceData
|
from core.emulator.emudata import InterfaceData
|
||||||
|
@ -25,12 +26,14 @@ from core.mobility import BasicRangeModel
|
||||||
from core.service import ServiceManager
|
from core.service import ServiceManager
|
||||||
|
|
||||||
CORE_LOCK = Lock()
|
CORE_LOCK = Lock()
|
||||||
|
coreemu = CoreEmu()
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.config["SECRET_KEY"] = "core"
|
|
||||||
socketio = SocketIO(app)
|
socketio = SocketIO(app)
|
||||||
|
app.config["SECRET_KEY"] = "core"
|
||||||
|
|
||||||
coreemu = CoreEmu()
|
mobility_routes.coreemu = coreemu
|
||||||
|
app.register_blueprint(mobility_routes.mobility_api, url_prefix="/sessions/<int:session_id>")
|
||||||
|
|
||||||
|
|
||||||
def synchronized(function):
|
def synchronized(function):
|
||||||
|
@ -120,19 +123,22 @@ def broadcast_event(event):
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def broadcast_node(node):
|
||||||
|
socketio.emit("node", {
|
||||||
|
"id": node.id,
|
||||||
|
"name": node.name,
|
||||||
|
"model": node.model,
|
||||||
|
"position": {
|
||||||
|
"x": node.x_position,
|
||||||
|
"y": node.y_position,
|
||||||
|
},
|
||||||
|
"services": node.services.split("|"),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
@socketio.on("connect")
|
@socketio.on("connect")
|
||||||
def websocket_connect():
|
def websocket_connect():
|
||||||
emit("info", {"message": "You are connected!"})
|
emit("info", {"message": "You are connected!"})
|
||||||
socketio.emit("node", {
|
|
||||||
"id": 1,
|
|
||||||
"x": 100,
|
|
||||||
"y": 101
|
|
||||||
})
|
|
||||||
socketio.emit("node", {
|
|
||||||
"id": 1,
|
|
||||||
"x": 100,
|
|
||||||
"y": 150
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
@socketio.on("disconnect")
|
@socketio.on("disconnect")
|
||||||
|
@ -142,7 +148,7 @@ def websocket_disconnect():
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
def home():
|
def home():
|
||||||
return render_template('index.html')
|
return render_template("index.html")
|
||||||
|
|
||||||
|
|
||||||
@app.route("/ips", methods=["POST"])
|
@app.route("/ips", methods=["POST"])
|
||||||
|
@ -233,6 +239,7 @@ def create_session():
|
||||||
|
|
||||||
# add handlers
|
# add handlers
|
||||||
session.event_handlers.append(broadcast_event)
|
session.event_handlers.append(broadcast_event)
|
||||||
|
session.node_handlers.append(broadcast_node)
|
||||||
|
|
||||||
response_data = jsonify(
|
response_data = jsonify(
|
||||||
id=session.session_id,
|
id=session.session_id,
|
||||||
|
|
63
webapp/mobility_routes.py
Normal file
63
webapp/mobility_routes.py
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
from flask import Blueprint
|
||||||
|
from flask import jsonify
|
||||||
|
from flask import request
|
||||||
|
|
||||||
|
from core.mobility import Ns2ScriptedMobility
|
||||||
|
|
||||||
|
mobility_api = Blueprint("mobility_api", __name__)
|
||||||
|
|
||||||
|
coreemu = None
|
||||||
|
|
||||||
|
|
||||||
|
@mobility_api.route("/nodes/<node_id>/mobility", methods=["POST"])
|
||||||
|
def set_mobility_config(session_id, node_id):
|
||||||
|
session = coreemu.sessions.get(session_id)
|
||||||
|
if not session:
|
||||||
|
return jsonify(error="session does not exist"), 404
|
||||||
|
|
||||||
|
if node_id.isdigit():
|
||||||
|
node_id = int(node_id)
|
||||||
|
|
||||||
|
data = request.get_json() or {}
|
||||||
|
|
||||||
|
session.mobility.set_model_config(node_id, Ns2ScriptedMobility.name, data)
|
||||||
|
|
||||||
|
return jsonify()
|
||||||
|
|
||||||
|
|
||||||
|
@mobility_api.route("/nodes/<node_id>/mobility")
|
||||||
|
def get_mobility_config(session_id, node_id):
|
||||||
|
session = coreemu.sessions.get(session_id)
|
||||||
|
if not session:
|
||||||
|
return jsonify(error="session does not exist"), 404
|
||||||
|
|
||||||
|
if node_id.isdigit():
|
||||||
|
node_id = int(node_id)
|
||||||
|
|
||||||
|
config = session.mobility.get_model_config(node_id, Ns2ScriptedMobility.name)
|
||||||
|
|
||||||
|
return jsonify(config)
|
||||||
|
|
||||||
|
|
||||||
|
@mobility_api.route("/nodes/<node_id>/mobility/<action>", methods=["PUT"])
|
||||||
|
def mobility_action(session_id, node_id, action):
|
||||||
|
session = coreemu.sessions.get(session_id)
|
||||||
|
if not session:
|
||||||
|
return jsonify(error="session does not exist"), 404
|
||||||
|
|
||||||
|
if node_id.isdigit():
|
||||||
|
node_id = int(node_id)
|
||||||
|
node = session.objects.get(node_id)
|
||||||
|
if not node:
|
||||||
|
return jsonify(error="node does not exist"), 404
|
||||||
|
|
||||||
|
if action == "start":
|
||||||
|
node.mobility.start()
|
||||||
|
elif action == "pause":
|
||||||
|
node.mobility.pause()
|
||||||
|
elif action == "stop":
|
||||||
|
node.mobility.stop(move_initial=True)
|
||||||
|
else:
|
||||||
|
return jsonify(error="invalid mobility action: %s" % action), 404
|
||||||
|
|
||||||
|
return jsonify()
|
Loading…
Reference in a new issue