web app, initial working emane option configuration
This commit is contained in:
parent
ddfa0ddfa4
commit
a55cd6a524
6 changed files with 262 additions and 3 deletions
|
@ -17,7 +17,7 @@ from core.emulator.coreemu import CoreEmu
|
|||
from core.emulator.emudata import InterfaceData
|
||||
from core.emulator.emudata import LinkOptions
|
||||
from core.emulator.emudata import NodeOptions
|
||||
from core.enumerations import EventTypes
|
||||
from core.enumerations import EventTypes, ConfigFlags
|
||||
from core.enumerations import LinkTypes
|
||||
from core.enumerations import NodeTypes
|
||||
from core.misc import nodeutils
|
||||
|
@ -200,7 +200,33 @@ def get_session(session_id):
|
|||
)
|
||||
|
||||
|
||||
@app.route("/sessions/<int:session_id>/emane")
|
||||
@app.route("/sessions/<int:session_id>/config", methods=["PUT"])
|
||||
@synchronized
|
||||
def set_config(session_id):
|
||||
session = coreemu.sessions.get(session_id)
|
||||
if not session:
|
||||
return jsonify(error="session does not exist"), 404
|
||||
|
||||
data = request.get_json() or {}
|
||||
name = data["name"]
|
||||
node_id = data.get("node")
|
||||
values = data["values"]
|
||||
data_types = [value["type"] for value in values]
|
||||
data_values = "|".join(["%s=%s" % (value["name"], value["value"]) for value in values])
|
||||
|
||||
config_data = ConfigData(
|
||||
node=node_id,
|
||||
object=name,
|
||||
type=ConfigFlags.NONE.value,
|
||||
data_types=tuple(data_types),
|
||||
data_values=data_values
|
||||
)
|
||||
logger.info("setting config: %s", config_data)
|
||||
session.config_object(config_data)
|
||||
return jsonify()
|
||||
|
||||
|
||||
@app.route("/sessions/<int:session_id>/emane/models")
|
||||
def get_emane_models(session_id):
|
||||
session = coreemu.sessions.get(session_id)
|
||||
if not session:
|
||||
|
@ -215,6 +241,69 @@ def get_emane_models(session_id):
|
|||
return jsonify(models=models)
|
||||
|
||||
|
||||
@app.route("/sessions/<int:session_id>/emane/options")
|
||||
def get_emane_options(session_id):
|
||||
session = coreemu.sessions.get(session_id)
|
||||
if not session:
|
||||
return jsonify(error="session does not exist"), 404
|
||||
|
||||
node_id = request.args.get("node")
|
||||
if node_id.isdigit():
|
||||
node_id = int(node_id)
|
||||
|
||||
config_data = ConfigData(
|
||||
node=node_id,
|
||||
object="emane",
|
||||
type=ConfigFlags.REQUEST.value
|
||||
)
|
||||
replies = session.config_object(config_data)
|
||||
if len(replies) != 1:
|
||||
return jsonify(error="failure getting emane options"), 404
|
||||
config_groups = replies[0]
|
||||
|
||||
captions = config_groups.captions.split("|")
|
||||
data_values = config_groups.data_values.split("|")
|
||||
possible_values = config_groups.possible_values.split("|")
|
||||
groups = config_groups.groups.split("|")
|
||||
|
||||
emane_options = []
|
||||
for i, data_type in enumerate(config_groups.data_types):
|
||||
data_value = data_values[i].split("=")
|
||||
value = None
|
||||
name = data_value[0]
|
||||
if len(data_value) == 2:
|
||||
value = data_value[1]
|
||||
|
||||
possible_value = possible_values[i]
|
||||
select = None
|
||||
if possible_value:
|
||||
select = possible_value.split(",")
|
||||
|
||||
label = captions[i]
|
||||
|
||||
emane_option = {
|
||||
"label": label,
|
||||
"name": name,
|
||||
"value": value,
|
||||
"type": data_type,
|
||||
"select": select
|
||||
}
|
||||
emane_options.append(emane_option)
|
||||
|
||||
config_groups = []
|
||||
for group in groups:
|
||||
name, indexes = group.split(":")
|
||||
indexes = [int(x) for x in indexes.split("-")]
|
||||
start = indexes[0] - 1
|
||||
stop = indexes[1]
|
||||
config_groups.append({
|
||||
"name": name,
|
||||
"options": emane_options[start: stop]
|
||||
})
|
||||
|
||||
return jsonify(groups=config_groups)
|
||||
|
||||
|
||||
@app.route("/sessions/<int:session_id>/nodes", methods=["POST"])
|
||||
@synchronized
|
||||
def create_node(session_id):
|
||||
|
|
|
@ -30,3 +30,12 @@
|
|||
.context .list-group-item {
|
||||
padding: .5rem 1.25rem;
|
||||
}
|
||||
|
||||
#config-modal {
|
||||
overflow-y: initial !important;
|
||||
}
|
||||
|
||||
#config-modal .modal-body {
|
||||
height: calc(100vh - 200px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
|
|
@ -66,7 +66,15 @@ class CoreRest {
|
|||
}
|
||||
|
||||
async getEmaneModels() {
|
||||
return await $.getJSON(`/sessions/${this.currentSession}/emane`);
|
||||
return await $.getJSON(`/sessions/${this.currentSession}/emane/models`);
|
||||
}
|
||||
|
||||
async getEmaneOptions(nodeId) {
|
||||
return await $.getJSON(`/sessions/${this.currentSession}/emane/options`, {node: nodeId});
|
||||
}
|
||||
|
||||
async setConfig(config) {
|
||||
return await putJson(`/sessions/${this.currentSession}/config`, config);
|
||||
}
|
||||
|
||||
async createNode(node) {
|
||||
|
|
|
@ -26,6 +26,114 @@ function createCheckbox(name, value, label, checked = false) {
|
|||
return $formCheck;
|
||||
}
|
||||
|
||||
function createTextbox(name, label, value) {
|
||||
const $formGroup = $('<div>', {class: 'form-group'});
|
||||
const $label = $('<label>', {text: label});
|
||||
const $input = $('<input>', {type: 'text', class: 'form-control', name, value});
|
||||
$formGroup.append([$label, $input]);
|
||||
return $formGroup;
|
||||
}
|
||||
|
||||
function createTabHeader(id, label, active) {
|
||||
let classes = 'nav-link';
|
||||
if (active) {
|
||||
classes += ' active';
|
||||
}
|
||||
const $li = $('<li>', {class: 'nav-item'});
|
||||
const $a = $('<a>', {class: classes, 'data-toggle': 'tab', href: `#${id}`, role: 'tab', text: label});
|
||||
$li.append($a);
|
||||
return $li;
|
||||
}
|
||||
|
||||
function createTabPane(id, active) {
|
||||
let classes = 'tab-pane fade';
|
||||
if (active) {
|
||||
classes += ' show active';
|
||||
}
|
||||
|
||||
return $('<div>', {class: classes, role: 'tabpanel', id});
|
||||
}
|
||||
|
||||
|
||||
class ConfigModel {
|
||||
constructor(coreRest) {
|
||||
this.coreRest = coreRest;
|
||||
this.$modal = $('#config-modal');
|
||||
this.$title = this.$modal.find('.modal-title');
|
||||
this.$tabHeaders = this.$modal.find('.nav-tabs');
|
||||
this.$tabContent = this.$modal.find('.tab-content');
|
||||
this.$form = this.$modal.find('form');
|
||||
this.$saveButton = $('#config-button');
|
||||
this.$saveButton.click(this.onClick.bind(this));
|
||||
this.$nodeEditModal = $('#nodeedit-modal');
|
||||
this.$nodeEditModal.on('click', '#emane-options', this.emaneOptionsClick.bind(this));
|
||||
this.$nodeEditModal.on('click', '#emane-model-options', this.emaneModelOptionsClick.bind(this));
|
||||
}
|
||||
|
||||
async onClick() {
|
||||
const nodeId = this.$nodeEditModal.data('node');
|
||||
const configType = this.$modal.data('type');
|
||||
console.log('config save clicked: ', nodeId, configType);
|
||||
const configs = [];
|
||||
for (let input of this.$form.find('input')) {
|
||||
const $input = $(input);
|
||||
const name = $input.attr('name');
|
||||
const dataType = $input.parent().data('type');
|
||||
const value = $input.val();
|
||||
configs.push({name, value, type: dataType});
|
||||
}
|
||||
console.log('config data: ', configs);
|
||||
await this.coreRest.setConfig({
|
||||
name: configType,
|
||||
node: nodeId,
|
||||
values: configs
|
||||
});
|
||||
this.$modal.modal('hide');
|
||||
}
|
||||
|
||||
async emaneOptionsClick(event) {
|
||||
this.$nodeEditModal.modal('hide');
|
||||
const nodeId = this.$nodeEditModal.data('node');
|
||||
this.$modal.data('type', 'emane');
|
||||
this.$title.text('EMANE Options');
|
||||
const config = await this.coreRest.getEmaneOptions(nodeId);
|
||||
console.log('emane options clicked: ', config);
|
||||
this.$tabHeaders.html('');
|
||||
this.$tabContent.html('');
|
||||
let initialTab = true;
|
||||
for (let group of config.groups) {
|
||||
let active = false;
|
||||
if (initialTab) {
|
||||
initialTab = false;
|
||||
active = true;
|
||||
}
|
||||
|
||||
console.log('option group: ', group.name);
|
||||
const tabId = group.name.toLocaleLowerCase().split(" ").join("-");
|
||||
console.log('tab id: ', tabId);
|
||||
const $tabHeader = createTabHeader(tabId, group.name, active);
|
||||
this.$tabHeaders.append($tabHeader);
|
||||
const $tabPane = createTabPane(tabId, active);
|
||||
this.$tabContent.append($tabPane);
|
||||
for (let option of group.options) {
|
||||
const $textbox = createTextbox(option.name, option.label, option.value);
|
||||
$textbox.data('type', option.type);
|
||||
$tabPane.append($textbox);
|
||||
}
|
||||
}
|
||||
this.$modal.modal('show');
|
||||
return false;
|
||||
}
|
||||
|
||||
async emaneModelOptionsClick(event) {
|
||||
this.$nodeEditModal.modal('hide');
|
||||
this.$title.text('EMANE Model Options');
|
||||
console.log('emane model clicked');
|
||||
this.$modal.modal('show');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ServiceModal {
|
||||
constructor(coreRest) {
|
||||
|
@ -274,6 +382,27 @@ class NodeEditModal {
|
|||
this.$formCustom.html('');
|
||||
if (node.type === CoreNodeHelper.emaneNode) {
|
||||
this.$formCustom.append($('<label>', {class: 'form-label', text: 'EMANE Model'}));
|
||||
// add buttons for editing emane and model options
|
||||
const $formRow = $('<div>', {class: 'row'});
|
||||
const $emaneButton = $('<div>', {class: 'col'}).append(
|
||||
$('<a>', {
|
||||
id: 'emane-options',
|
||||
href: '#',
|
||||
class: 'btn btn-primary btn-sm w-100',
|
||||
text: 'EMANE Options'
|
||||
}
|
||||
));
|
||||
const $modelButton = $('<div>', {class: 'col'}).append(
|
||||
$('<a>', {
|
||||
id: 'emane-model-options',
|
||||
href: '#',
|
||||
class: 'btn btn-primary btn-sm w-100',
|
||||
text: 'Model Options'
|
||||
}
|
||||
));
|
||||
$formRow.append([$emaneButton, $modelButton]);
|
||||
this.$formCustom.append($formRow);
|
||||
|
||||
for (let model of this.coreNetwork.emaneModels) {
|
||||
const checked = node.emane === model;
|
||||
const label = model.split('_')[1];
|
||||
|
|
22
webapp/templates/config_modal.html
Normal file
22
webapp/templates/config_modal.html
Normal file
|
@ -0,0 +1,22 @@
|
|||
<div id="config-modal" class="modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-dark text-white">
|
||||
<h5 class="modal-title"></h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body h-80">
|
||||
<ul class="nav nav-tabs"></ul>
|
||||
<form>
|
||||
<div class="tab-content p-1"></div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button id="config-button" type="button" class="btn btn-primary">Save</button>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -118,6 +118,7 @@
|
|||
{% include 'linkedit_modal.html' %}
|
||||
{% include 'service_modal.html' %}
|
||||
{% include 'services_modal.html' %}
|
||||
{% include 'config_modal.html' %}
|
||||
|
||||
<div id="node-context" class="list-group context d-none">
|
||||
<button type="button" class="list-group-item list-group-item-action" data-option="edit">Edit Node</button>
|
||||
|
@ -186,6 +187,7 @@
|
|||
const edgeEditModal = new EdgeEditModal(coreNetwork, coreRest);
|
||||
const edgeContext = new EdgeContext(coreNetwork, edgeEditModal);
|
||||
const infoPanel = new InfoPanel(coreNetwork);
|
||||
const configModal = new ConfigModel(coreRest);
|
||||
|
||||
coreNetwork.initialSession()
|
||||
.then(function (session) {
|
||||
|
|
Loading…
Reference in a new issue