web app, initial working emane option configuration

This commit is contained in:
Blake J. Harnden 2018-05-17 20:38:32 -07:00
parent ddfa0ddfa4
commit a55cd6a524
6 changed files with 262 additions and 3 deletions

View file

@ -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):

View file

@ -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;
}

View file

@ -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) {

View file

@ -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];

View 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">&times;</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>

View file

@ -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) {