first initial working link for ptp and joining back and drawing the edge, also making use of old core icons to provide a basic look and feel, updated coloring to dark mode instead of info, seems to fit better

This commit is contained in:
Blake J. Harnden 2018-05-07 15:04:54 -07:00
parent b10c7fe502
commit 8e99af96a4
18 changed files with 1414 additions and 45 deletions

View file

@ -320,7 +320,9 @@ class IpPrefix(object):
addr = ""
prefix_endpoint = -1
print "stop condition: %s" % (-(self.addrlen >> 3) - 1)
for i in xrange(-1, -(self.addrlen >> 3) - 1, -1):
print "i: %s" % i
prefix_endpoint = i
addr = chr(ord(self.prefix[i]) | (tmp & 0xff)) + addr
tmp >>= 8

View file

@ -1,3 +1,8 @@
import os
from functools import wraps
from threading import Lock
from flask import Flask
from flask import jsonify
from flask import render_template
@ -7,13 +12,16 @@ from flask_socketio import emit
from core import logger
from core.emulator.coreemu import CoreEmu
from core.emulator.emudata import InterfaceData
from core.emulator.emudata import InterfaceData, IpPrefixes
from core.emulator.emudata import LinkOptions
from core.emulator.emudata import NodeOptions
from core.enumerations import EventTypes
from core.enumerations import LinkTypes
from core.enumerations import NodeTypes
from core.misc import nodeutils
from core.misc.ipaddress import Ipv4Prefix, Ipv6Prefix
CORE_LOCK = Lock()
app = Flask(__name__)
app.config["SECRET_KEY"] = "core"
@ -22,6 +30,17 @@ socketio = SocketIO(app)
coreemu = CoreEmu()
def synchronized(function):
global CORE_LOCK
@wraps(function)
def wrapper(*args, **kwargs):
with CORE_LOCK:
return function(*args, **kwargs)
return wrapper
def link_data_str(link, key):
value = link.get(key)
if value:
@ -53,29 +72,53 @@ def home():
return render_template('index.html')
@app.route("/ips", methods=["POST"])
def get_ips():
data = request.get_json() or {}
node_id = data["id"]
node_id = int(node_id)
ip4_prefix = data.get("ip4")
ip6_prefix = data.get("ip6")
ip4_prefixes = Ipv4Prefix(ip4_prefix)
ip6_prefixes = Ipv6Prefix(ip6_prefix)
return jsonify(
ip4=str(ip4_prefixes.addr(node_id)),
ip4mask=ip4_prefixes.prefixlen,
ip6=str(ip6_prefixes.addr(node_id)),
ip6mask=ip6_prefixes.prefixlen
)
@app.route("/sessions")
def get_sessions():
sessions = []
for session in coreemu.sessions.itervalues():
sessions.append({
"id": session.session_id,
"state": session.state,
"nodes": session.get_node_count()
})
return jsonify(sessions=sessions)
@app.route("/sessions", methods=["POST"])
@synchronized
def create_session():
session = coreemu.create_session()
session.set_state(EventTypes.CONFIGURATION_STATE)
response_data = jsonify(
id=session.session_id,
state=session.state,
url="/sessions/%s" % session.session_id
)
return response_data, 201
@app.route("/sessions/<int:session_id>", methods=["DELETE"])
@synchronized
def delete_session(session_id):
result = coreemu.delete_session(session_id)
if result:
@ -96,6 +139,7 @@ def get_session(session_id):
"id": node.objid,
"name": node.name,
"type": nodeutils.get_node_type(node.__class__).value,
"model": getattr(node, "type", None),
"position": {
"x": node.position.x,
"y": node.position.y,
@ -112,12 +156,14 @@ def get_session(session_id):
@app.route("/sessions/<int:session_id>/nodes", methods=["POST"])
@synchronized
def create_node(session_id):
session = coreemu.sessions.get(session_id)
if not session:
return jsonify(error="session does not exist"), 404
data = request.get_json() or {}
node_id = data.get("id")
node_type = data.get("type", NodeTypes.DEFAULT.value)
node_type = NodeTypes(node_type)
logger.info("creating node: %s - %s", node_type.name, data)
@ -130,7 +176,7 @@ def create_node(session_id):
node_options.opaque = data.get("opaque")
node_options.set_position(data.get("x"), data.get("y"))
node_options.set_location(data.get("lat"), data.get("lon"), data.get("alt"))
node = session.add_node(_type=node_type, node_options=node_options)
node = session.add_node(_type=node_type, _id=node_id, node_options=node_options)
return jsonify(
id=node.objid,
url="/sessions/%s/nodes/%s" % (session_id, node.objid)
@ -174,6 +220,7 @@ def get_node(session_id, node_id):
@app.route("/sessions/<int:session_id>/nodes/<node_id>", methods=["DELETE"])
@synchronized
def delete_node(session_id, node_id):
session = coreemu.sessions.get(session_id)
if not session:
@ -193,6 +240,7 @@ def delete_node(session_id, node_id):
@app.route("/sessions/<int:session_id>/state", methods=["PUT"])
@synchronized
def set_session_state(session_id):
session = coreemu.sessions.get(session_id)
if not session:
@ -204,6 +252,9 @@ def set_session_state(session_id):
session.set_state(state)
if state == EventTypes.INSTANTIATION_STATE:
# create session directory if it does not exist
if not os.path.exists(session.session_dir):
os.mkdir(session.session_dir)
session.instantiate()
elif state == EventTypes.SHUTDOWN_STATE:
session.shutdown()
@ -218,6 +269,7 @@ def set_session_state(session_id):
@app.route("/sessions/<int:session_id>/links", methods=["POST"])
@synchronized
def add_link(session_id):
session = coreemu.sessions.get(session_id)
if not session:
@ -282,6 +334,7 @@ def add_link(session_id):
@app.route("/sessions/<int:session_id>/links", methods=["DELETE"])
@synchronized
def delete_link(session_id):
session = coreemu.sessions.get(session_id)
if not session:

File diff suppressed because one or more lines are too long

BIN
webapp/static/core-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View file

@ -3,8 +3,7 @@
}
.navbar-text {
color: #17a2b8 !important;
background-color: #fff;
color: #fff !important;
font-weight: bold;
}

199
webapp/static/coreip.js Normal file
View file

@ -0,0 +1,199 @@
const AF_INET = 2;
const AF_INET6 = 23;
function ord (string) {
var str = string + '';
var code = str.charCodeAt(0);
if (code >= 0xD800 && code <= 0xDBFF) {
// High surrogate (could change last hex to 0xDB7F to treat
// high private surrogates as single characters)
var hi = code;
if (str.length === 1) {
// This is just a high surrogate with no following low surrogate,
// so we return its value;
return code;
// we could also throw an error as it is not a complete character,
// but someone may want to know
}
var low = str.charCodeAt(1);
return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000
}
if (code >= 0xDC00 && code <= 0xDFFF) {
// Low surrogate
// This is just a low surrogate with no preceding high surrogate,
// so we return its value;
return code;
// we could also throw an error as it is not a complete character,
// but someone may want to know
}
return code
}
function inetNtop (a) {
var i = 0;
var m = '';
var c = [];
a += '';
if (a.length === 4) {
// IPv4
return [
a.charCodeAt(0),
a.charCodeAt(1),
a.charCodeAt(2),
a.charCodeAt(3)
].join('.');
} else if (a.length === 16) {
// IPv6
for (i = 0; i < 16; i++) {
c.push(((a.charCodeAt(i++) << 8) + a.charCodeAt(i)).toString(16));
}
return c.join(':')
.replace(/((^|:)0(?=:|$))+:?/g, function (t) {
m = (t.length > m.length) ? t : m;
return t;
})
.replace(m || ' ', '::');
} else {
// Invalid length
return false;
}
}
function inetPton(a) {
var r;
var m;
var x;
var i;
var j;
var f = String.fromCharCode;
// IPv4
m = a.match(/^(?:\d{1,3}(?:\.|$)){4}/);
if (m) {
m = m[0].split('.');
m = f(m[0]) + f(m[1]) + f(m[2]) + f(m[3]);
// Return if 4 bytes, otherwise false.
return m.length === 4 ? m : false;
}
r = /^((?:[\da-f]{1,4}(?::|)){0,8})(::)?((?:[\da-f]{1,4}(?::|)){0,8})$/;
// IPv6
m = a.match(r);
if (m) {
// Translate each hexadecimal value.
for (j = 1; j < 4; j++) {
// Indice 2 is :: and if no length, continue.
if (j === 2 || m[j].length === 0) {
continue;
}
m[j] = m[j].split(':');
for (i = 0; i < m[j].length; i++) {
m[j][i] = parseInt(m[j][i], 16);
// Would be NaN if it was blank, return false.
if (isNaN(m[j][i])) {
// Invalid IP.
return false;
}
m[j][i] = f(m[j][i] >> 8) + f(m[j][i] & 0xFF);
}
m[j] = m[j].join('');
}
x = m[1].length + m[3].length;
if (x === 16) {
return m[1] + m[3];
} else if (x < 16 && m[2].length > 0) {
return m[1] + (new Array(16 - x + 1))
.join('\x00') + m[3];
}
}
// Invalid IP
return false
}
class IpAddress {
constructor(addressFamily, address) {
console.log('ip address: ', addressFamily, address);
this.addressFamily = addressFamily;
this.address = address;
}
str() {
return inetNtop(this.address);
}
}
class IpPrefix {
constructor(addressFamily, prefix) {
this.addressFamily = addressFamily;
if (this.addressFamily === AF_INET) {
this.addressLength = 32;
} else if (this.addressFamily == AF_INET6) {
this.addressLength = 128;
} else {
throw Error(`invalid address family: ${addressFamily}`);
}
const values = prefix.split('/');
console.log(`split prefix: ${values}`);
if (values.length > 2) {
throw Error(`invalid prefix: ${prefix}`);
}
if (values.length === 2) {
this.prefixLength = parseInt(values[1]);
} else {
this.prefixLength = this.addressLength;
}
this.prefix = inetPton(values[0]);
if (this.addressLength > this.prefixLength) {
const addressBits = this.addressLength - this.prefixLength;
let netMask = ((1 << this.prefixLength) - 1) << addressBits;
prefix = '';
//in xrange(-1, -(addrbits >> 3) - 2, -1)
const stopValue = -(addressBits) - 2;
for (let i = -1; i > stopValue; --i) {
prefix = String.fromCharCode(ord(this.prefix[i]) & (netMask & 0xff)) + prefix;
netMask >>= 8;
}
this.prefix = this.prefix.slice(0, stopValue) + prefix;
}
}
getAddress(id) {
if (id in [-1, 0, 1] && this.addressLength === this.prefixLength) {
return new IpAddress(this.addressFamily, this.prefix);
}
const value = (1 << (this.addressLength - this.prefixLength)) - 1;
console.log(`address length(${this.addressLength}) prefix length(${this.prefixLength}) value(${value})`);
if (id === 0 || id > value || (this.addressFamily === AF_INET && id === value)) {
throw Error(`invalid id for prefix ${this.prefix}: ${id}`);
}
let address = '';
let prefixEndpoint = -1;
console.log('stop condition: ', -(this.addressLength >> 3) - 1);
for (let i = -1; i > -(this.addressLength >> 3) - 1; --i) {
console.log('i: ', i);
prefixEndpoint = i;
address = String.fromCharCode(ord(this.prefix[i]) | (id & 0xff)) + address;
console.log('address: ', address);
id >>= 8;
console.log('id: ', id);
if (!id) {
break
}
}
address = this.prefix.slice(0, prefixEndpoint) + address;
return new IpAddress(this.addressFamily, address);
}
}

View file

@ -1,25 +1,52 @@
const Ip4Prefix = '10.0.0.1/24';
const Ip6Prefix = '2001::/64';
const PtpNode = 12;
const NodeTypes = {
// default router
0: {
name: 'node'
name: 'node',
display: 'Default'
},
// switch
4: {
name: 'switch'
name: 'switch',
display: 'Switch'
},
// hub
5: {
name: 'hub'
name: 'hub',
display: 'Hub'
},
// wlan
6: {
name: 'wlan'
name: 'wlan',
display: 'WLAN'
},
12: {
name: 'ptp',
display: 'PTP'
}
};
function getNodeType(nodeType) {
return NodeTypes[nodeType];
}
const NodeIcons = {
router: 'static/router.svg',
host: 'static/host.gif',
PC: 'static/pc.gif',
mdr: 'static/mdr.svg',
switch: 'static/lanswitch.svg',
hub: 'static/hub.svg',
wlan: 'static/wlan.gif'
};
class CoreNode {
constructor(id, name, x, y) {
constructor(id, type, name, x, y) {
this.id = id;
this.type = type;
this.name = name;
this.model = null;
this.canvas = null;
@ -32,35 +59,54 @@ class CoreNode {
this.lon = null;
this.alt = null;
this.emulation_id = null;
this.emulation_server = null;
this.emulation_server = null
this.interfaces = {};
}
getNetworkNode() {
let iconName = getNodeType(this.type).name;
if (this.type === 0) {
iconName = this.model;
}
const icon = NodeIcons[iconName];
return {
id: this.id,
x: this.x,
y: this.y,
label: this.name,
node: this
coreNode: this,
//color: '#FFF',
//shape: 'image',
//shapeProperties: {
// useBorderWithImage: true
//},
//image: nodeMode.image,
//type: nodeMode.nodeType
shape: 'image',
image: icon
};
}
json() {
return {
id: this.id,
type: this.type,
name: this.name,
model: this.model,
x: this.x,
y: this.y,
lat: this.lat,
lon: this.lon,
alt: this.alt
}
}
}
class CoreNetwork {
constructor(elementId) {
this.nodeType = NodeTypes['0'];
this.nodeModel = null;
constructor(elementId, coreRest) {
this.coreRest = coreRest;
this.nodeType = 0;
this.nodeModel = 'router';
this.nodeId = 0;
this.container = document.getElementById(elementId);
this.nodes = new vis.DataSet();
this.edges = new vis.DataSet();
this.links = {};
this.networkData = {
nodes: this.nodes,
edges: this.edges
@ -88,18 +134,120 @@ class CoreNetwork {
this.edges.on('add', this.addEdge.bind(this));
}
getCoreNodes() {
const coreNodes = [];
for (let node of this.nodes.get()) {
coreNodes.push(node.coreNode.json());
}
return coreNodes;
}
addCoreNode(node) {
const position = node.position;
const coreNode = new CoreNode(node.id, node.type, node.name, position.x, position.y);
coreNode.model = node.model;
this.nodes.add(coreNode.getNetworkNode());
}
nextNodeId() {
this.nodeId += 1;
return this.nodeId;
}
joinedSessions(nodes) {
const self = this;
for (let node of nodes) {
if (node.type === PtpNode) {
continue;
}
this.addCoreNode(node);
}
for (let node of nodes) {
if (![4, 5, 6, 12].includes(node.type)) {
continue;
}
this.coreRest.getLinks(node.id)
.then(function(response) {
console.log('link response: ', response);
for (let linkData of response.links) {
self.createEdgeFromLink(linkData);
}
})
.catch(function(err) {
console.log('get link error: ', err);
});
}
if (nodes.length) {
this.nodeId = Math.max.apply(Math, nodes.map(function (node) {
return node.id
}));
} else {
this.nodeId = 0;
}
}
createEdgeFromLink(linkData) {
const fromNode = this.nodes.get(linkData.node1_id).coreNode;
const toNode = this.nodes.get(linkData.node2_id).coreNode;
const linkId = `${fromNode.id}-${toNode.id}`;
const interfaceOne = {
id: linkData.interface1_id,
ip4: linkData.interface1_ip4,
ip4mask: linkData.interface1_ip4_mask,
ip6: linkData.interface1_ip6,
ip6mask: linkData.interface1_ip6_mask
};
fromNode.interfaces[linkData.interface1_id] = interfaceOne;
const interfaceTwo = {
id: linkData.interface2_id,
ip4: linkData.interface2_ip4,
ip4mask: linkData.interface2_ip4_mask,
ip6: linkData.interface2_ip6,
ip6mask: linkData.interface2_ip6_mask
};
toNode.interfaces[linkData.interface2_id] = interfaceTwo;
this.links[linkId] = {
node_one: fromNode.id,
node_two: toNode.id,
interface_one: interfaceOne,
interface_two: interfaceTwo
};
const edge = {from: fromNode.id, to: toNode.id};
this.edges.add(edge);
}
async start() {
const nodes = coreNetwork.getCoreNodes();
for (let node of nodes) {
const response = await coreRest.createNode(node);
console.log('created node: ', response);
}
for (let linkId in this.links) {
const link = this.links[linkId];
const response = await coreRest.createLink(link);
console.log('created link: ', response);
}
return await coreRest.setSessionState(SessionStates.instantiation);
}
addNode(properties) {
console.log('add node event: ', properties);
if (properties.nodes.length === 0) {
const {x, y} = properties.pointer.canvas;
const nodeId = this.nextNodeId();
const name = `${this.nodeType.name}${nodeId}`;
const coreNode = new CoreNode(nodeId, name, x, y);
const nodeTypeData = getNodeType(this.nodeType);
const name = `${nodeTypeData.name}${nodeId}`;
const coreNode = new CoreNode(nodeId, this.nodeType, name, x, y);
coreNode.model = this.nodeModel;
this.nodes.add(coreNode.getNetworkNode());
console.log('added node: ', coreNode.getNetworkNode());
@ -115,10 +263,55 @@ class CoreNetwork {
this.edges.remove(edge.id);
}
const fromNode = this.nodes.get(edge.from).coreNode;
const toNode = this.nodes.get(edge.to).coreNode;
this.addEdgeLink(edge, fromNode, toNode)
.then(function() {
console.log('create edge link success!');
})
.catch(function(err) {
console.log('create link error: ', err);
});
// keep edge mode enabled
setTimeout(() => this.network.addEdgeMode(), 250);
}
async addEdgeLink(edge, fromNode, toNode) {
const fromIps = await this.coreRest.getNodeIps(fromNode.id, Ip4Prefix, Ip6Prefix);
const toIps = await this.coreRest.getNodeIps(toNode.id, Ip4Prefix, Ip6Prefix);
console.log('link ips: ', fromIps, toIps);
const linkId = `${fromNode.id}-${toNode.id}`;
const interfaceOneId =Object.keys(fromNode.interfaces).length;
const interfaceOne = {
id: interfaceOneId,
ip4: fromIps.ip4,
ip4mask: fromIps.ip4mask,
ip6: fromIps.ip6,
ip6mask: fromIps.ip6mask
};
fromNode.interfaces[interfaceOneId] = interfaceOne;
const interfaceTwoId = Object.keys(toNode.interfaces).length;
const interfaceTwo = {
id: interfaceTwoId,
ip4: toIps.ip4,
ip4mask: toIps.ip4mask,
ip6: toIps.ip6,
ip6mask: toIps.ip6mask
};
toNode.interfaces[interfaceTwoId] = interfaceTwo;
this.links[linkId] = {
node_one: fromNode.id,
node_two: toNode.id,
interface_one: interfaceOne,
interface_two: interfaceTwo
};
}
linkMode(enabled) {
console.log('link mode:', enabled);
if (enabled) {
@ -129,7 +322,7 @@ class CoreNetwork {
}
setNodeMode(nodeType, model) {
this.nodeType = NodeTypes[nodeType];
this.nodeType = nodeType;
this.nodeModel = model || null;
}
}

View file

@ -1,9 +1,106 @@
const SessionStates = {
definition: 1,
configuration: 2,
instantiation: 3,
runtime: 4,
dataCollect: 5,
shutdown: 6
};
const SessionStateDisplay = {
1: 'Definition',
2: 'Configuration',
3: 'Instantiation',
4: 'Runtime',
5: 'Data Collect',
6: 'Shutdown'
};
async function sendJson(url, data, type) {
return await $.ajax({
url,
type,
data: JSON.stringify(data),
contentType: 'application/json',
dataType: 'json'
});
}
async function postJson(url, data) {
console.log('POST: ', url);
return await sendJson(url, data, 'POST');
}
async function putJson(url, data) {
console.log('PUT: ', url);
return await sendJson(url, data, 'PUT');
}
class CoreRest {
constructor() {
this.currentSession = null;
}
async sessions(callback) {
const response = await $.getJSON('/sessions');
callback(response);
getStateName(state) {
return SessionStateDisplay[state];
}
async getSession() {
return await $.getJSON(`/sessions/${this.currentSession}`);
}
async getSessions() {
return await $.getJSON('/sessions');
}
async createSession() {
return await postJson('/sessions');
}
async shutdownSession() {
return await this.setSessionState(SessionStates.shutdown);
}
async setSessionState(state) {
return await putJson(`/sessions/${this.currentSession}/state`, {state})
}
async createNode(node) {
return await postJson(`/sessions/${this.currentSession}/nodes`, node);
}
async createLink(link) {
return await postJson(`/sessions/${this.currentSession}/links`, link);
}
async getLinks(nodeId) {
return await $.getJSON(`/sessions/${this.currentSession}/nodes/${nodeId}/links`)
}
async getNodeIps(nodeId, ip4Prefix, ip6Prefix) {
return await postJson('/ips', {
id: nodeId,
ip4: ip4Prefix,
ip6: ip6Prefix
});
}
async retrieveSession() {
let response = await this.getSessions();
const sessions = response.sessions;
console.log('current sessions: ', sessions);
const session = {id: 0, state: 0};
if (sessions.length) {
this.currentSession = sessions[0].id;
session.state = sessions[0].state;
} else {
response = await this.createSession();
this.currentSession = response.id;
session.state = response.state;
}
session.id = this.currentSession;
return session;
}
}

BIN
webapp/static/host.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

197
webapp/static/hub.svg Normal file
View file

@ -0,0 +1,197 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="146"
height="100"
id="svg3868"
version="1.1"
inkscape:version="0.47 r22583"
sodipodi:docname="hub.svg">
<defs
id="defs3870">
<linearGradient
id="linearGradient5149">
<stop
id="stop5151"
offset="0"
style="stop-color:#5dacd1;stop-opacity:1;" />
<stop
id="stop5153"
offset="1"
style="stop-color:#1b4a78;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient4902">
<stop
style="stop-color:#484848;stop-opacity:1;"
offset="0"
id="stop4904" />
<stop
style="stop-color:#8f8f90;stop-opacity:0;"
offset="1"
id="stop4906" />
</linearGradient>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective3876" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient12001"
id="linearGradient4908"
x1="5.8083773"
y1="68.180191"
x2="84.600281"
y2="68.180191"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.269166,0,0,1.2912385,-2.3717949,931.82545)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient12001"
id="linearGradient5039"
x1="105"
y1="55"
x2="140"
y2="55"
gradientUnits="userSpaceOnUse" />
<inkscape:perspective
id="perspective5049"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient12001"
id="linearGradient3844"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.471308,0,0,0.471308,9.3066001,-238.48173)"
x1="175.71875"
y1="737.01562"
x2="470.00089"
y2="737.01562" />
<linearGradient
id="linearGradient12001">
<stop
style="stop-color:#1b4a78;stop-opacity:1;"
offset="0"
id="stop12003" />
<stop
style="stop-color:#5dacd1;stop-opacity:1;"
offset="1"
id="stop12005" />
</linearGradient>
<inkscape:perspective
id="perspective5135"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective5165"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="91.162404"
inkscape:cy="35.126778"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="914"
inkscape:window-height="668"
inkscape:window-x="2157"
inkscape:window-y="214"
inkscape:window-maximized="0">
<inkscape:grid
type="xygrid"
id="grid4935"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true"
spacingx="0.5px"
spacingy="0.5px" />
</sodipodi:namedview>
<metadata
id="metadata3873">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-952.36218)">
<rect
style="fill:url(#linearGradient4908);fill-opacity:1.0;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none"
id="rect4390"
width="100"
height="45"
x="5"
y="997.36218" />
<path
style="fill:url(#linearGradient5039);stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;fill-opacity:1.0"
d="m 105,45 35,-40 0,45 -35,40 0,-45 z"
id="path5031"
transform="translate(0,952.36218)"
sodipodi:nodetypes="ccccc" />
<path
style="fill:#3a78a0;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
d="M 105,45 5,45 40,5 140,5 105,45 z"
id="path5079"
transform="translate(0,952.36218)" />
<path
style="fill:#f2fdff;fill-opacity:0.70980392000000003;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="m 32.5,979.86218 7.5,-7.5 77.5,0 -7.5,7.5 -77.5,0 z"
id="path5255" />
<path
style="fill:#f2fdff;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:0.70980394"
d="m 45,27.5 -7.5,7.5 10,0 7.5,-7.5 -10,0 z"
id="path5259"
transform="translate(0,952.36218)" />
<path
id="path5261"
d="m 77.5,979.86218 -7.5,7.5 10,0 7.5,-7.5 -10,0 z"
style="fill:#f2fdff;fill-opacity:0.70980392;stroke:none" />
<path
style="fill:#f2fdff;fill-opacity:0.70980392;stroke:none"
d="m 70,964.86218 -7.5,7.5 10,0 7.5,-7.5 -10,0 z"
id="path5263" />
<path
id="path5265"
d="m 102.5,964.86218 -7.5,7.5 10,0 7.5,-7.5 -10,0 z"
style="fill:#f2fdff;fill-opacity:0.70980392;stroke:none" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.4 KiB

190
webapp/static/lanswitch.svg Normal file
View file

@ -0,0 +1,190 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="146"
height="100"
id="svg3868"
version="1.1"
inkscape:version="0.47 r22583"
sodipodi:docname="switch.svg">
<defs
id="defs3870">
<linearGradient
id="linearGradient5149">
<stop
id="stop5151"
offset="0"
style="stop-color:#5dacd1;stop-opacity:1;" />
<stop
id="stop5153"
offset="1"
style="stop-color:#1b4a78;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient4902">
<stop
style="stop-color:#484848;stop-opacity:1;"
offset="0"
id="stop4904" />
<stop
style="stop-color:#8f8f90;stop-opacity:0;"
offset="1"
id="stop4906" />
</linearGradient>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective3876" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient12001"
id="linearGradient4908"
x1="5.8083773"
y1="68.180191"
x2="84.600281"
y2="68.180191"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.269166,0,0,1.2912385,-2.3717949,931.82545)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient12001"
id="linearGradient5039"
x1="105"
y1="55"
x2="140"
y2="55"
gradientUnits="userSpaceOnUse" />
<inkscape:perspective
id="perspective5049"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient12001"
id="linearGradient3844"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.471308,0,0,0.471308,9.3066001,-238.48173)"
x1="175.71875"
y1="737.01562"
x2="470.00089"
y2="737.01562" />
<linearGradient
id="linearGradient12001">
<stop
style="stop-color:#1b4a78;stop-opacity:1;"
offset="0"
id="stop12003" />
<stop
style="stop-color:#5dacd1;stop-opacity:1;"
offset="1"
id="stop12005" />
</linearGradient>
<inkscape:perspective
id="perspective5135"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective5165"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.8284271"
inkscape:cx="91.162404"
inkscape:cy="35.126778"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="809"
inkscape:window-height="852"
inkscape:window-x="605"
inkscape:window-y="185"
inkscape:window-maximized="0">
<inkscape:grid
type="xygrid"
id="grid4935"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata3873">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-952.36218)">
<rect
style="fill:url(#linearGradient4908);fill-opacity:1.0;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none"
id="rect4390"
width="100"
height="45"
x="5"
y="997.36218" />
<path
style="fill:url(#linearGradient5039);stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;fill-opacity:1.0"
d="m 105,45 35,-40 0,45 -35,40 0,-45 z"
id="path5031"
transform="translate(0,952.36218)"
sodipodi:nodetypes="ccccc" />
<path
style="fill:#3a78a0;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
d="M 105,45 5,45 40,5 140,5 105,45 z"
id="path5079"
transform="translate(0,952.36218)" />
<path
style="fill:#f2fdff;fill-opacity:0.71171169;stroke:none"
d="m 63.720704,982.3378 12.656877,-3.10116 12.656905,-3.10105 -3.523923,3.28427 18.978167,0.15164 -6.571234,6.1243 -18.978156,-0.15164 -3.523925,3.28426 -5.843452,-3.24896 -5.851259,-3.24166 z"
id="path13511" />
<path
style="fill:#f2fdff;fill-opacity:0.71171169;stroke:none"
d="M 80.174263,970.7443 74.367199,967.43074 68.560125,964.11719 65,967.36218 46.024691,967.0001 l -6.638732,6.05106 18.975309,0.36208 -3.560123,3.24499 12.690524,-2.96052 12.682595,-2.95341 z"
id="path13513" />
<path
id="path5195"
d="M 65.174263,985.7443 59.367199,982.43074 53.560125,979.11719 50,982.36218 31.024691,982.0001 l -6.638732,6.05106 18.975309,0.36208 -3.560123,3.24499 12.690524,-2.96052 12.682595,-2.95341 z"
style="fill:#f2fdff;fill-opacity:0.71171169;stroke:none" />
<path
id="path5209"
d="m 78.9675,967.75499 12.656877,-3.10116 12.656903,-3.10105 -3.52392,3.28427 18.97816,0.15164 -6.57123,6.1243 -18.978154,-0.15164 -3.523925,3.28426 -5.843452,-3.24896 -5.851259,-3.24166 z"
style="fill:#f2fdff;fill-opacity:0.71171169;stroke:none" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.5 KiB

178
webapp/static/mdr.svg Normal file
View file

@ -0,0 +1,178 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="146"
height="100"
id="svg13653"
sodipodi:version="0.32"
inkscape:version="0.47 r22583"
sodipodi:docname="router.svg"
version="1.0">
<defs
id="defs13655">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 99.931252 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="199.10001 : 99.931252 : 1"
inkscape:persp3d-origin="99.550003 : 66.620834 : 1"
id="perspective3835" />
<linearGradient
id="linearGradient12828">
<stop
id="stop12830"
offset="0"
style="stop-color:#484849;stop-opacity:1;" />
<stop
style="stop-color:#434344;stop-opacity:1;"
offset="0"
id="stop12862" />
<stop
id="stop12832"
offset="1.0000000"
style="stop-color:#8f8f90;stop-opacity:0.0000000;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient12828"
id="radialGradient13651"
cx="328.57144"
cy="602.7193"
fx="328.57144"
fy="602.7193"
r="147.14285"
gradientTransform="matrix(1,0,0,0.177184,0,495.9268)"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient12001">
<stop
style="stop-color:#1b4a78;stop-opacity:1;"
offset="0"
id="stop12003" />
<stop
style="stop-color:#5dacd1;stop-opacity:1;"
offset="1"
id="stop12005" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient12001"
id="linearGradient13633"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.471308,0,0,0.471308,118.8781,123.5182)"
x1="175.71875"
y1="737.01562"
x2="470.00089"
y2="737.01562" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient12001"
id="linearGradient3844"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.471308,0,0,0.471308,-45.6934,-239.9103)"
x1="175.71875"
y1="737.01562"
x2="470.00089"
y2="737.01562" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="118.57814"
inkscape:cy="50.488033"
inkscape:document-units="px"
inkscape:current-layer="layer1"
inkscape:window-width="1280"
inkscape:window-height="949"
inkscape:window-x="1631"
inkscape:window-y="29"
showgrid="false"
inkscape:window-maximized="0" />
<metadata
id="metadata13658">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Capa 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-33.124945,-44.636248)">
<path
style="fill:url(#linearGradient3844);fill-opacity:1;stroke:none"
d="m 37.14136,72.27878 0,0.29457 c 0.006,-0.0975 0.0206,-0.19729 0.0295,-0.29457 l -0.0295,0 z m 138.62351,0 c 0.0302,0.33044 0.0589,0.66821 0.0589,1.00153 l 0,-1.00153 -0.0589,0 z m 0.0589,1.00153 c -1e-5,15.05224 -31.07495,27.26223 -69.35594,27.26223 -37.68286,1e-5 -68.3765,-11.82771 -69.32649,-26.55527 l 0,40.67979 c -0.0151,0.23376 -0.0147,0.45704 -0.0147,0.69223 0,0.22546 8.7e-4,0.45335 0.0147,0.67751 0.91151,14.74102 31.61889,26.59945 69.32649,26.59945 37.7076,0 68.41498,-11.85843 69.32648,-26.59945 l 0.0295,0 0,-0.50077 c 9.5e-4,-0.0587 0,-0.11794 0,-0.17674 0,-0.0588 9.4e-4,-0.11803 0,-0.17674 l 0,-41.90224 z"
id="path13626" />
<path
sodipodi:type="arc"
style="fill:#3a78a0;fill-opacity:1;stroke:none"
id="path11090"
sodipodi:cx="328.57144"
sodipodi:cy="602.7193"
sodipodi:rx="147.14285"
sodipodi:ry="26.071428"
d="m 475.71429,602.7193 c 0,14.39885 -65.87809,26.07143 -147.14285,26.07143 -81.26475,0 -147.14285,-11.67258 -147.14285,-26.07143 0,-14.39885 65.8781,-26.07143 147.14285,-26.07143 81.26476,0 147.14285,11.67258 147.14285,26.07143 z"
transform="matrix(0.471308,0,0,1.045917,-48.3838,-554.9944)" />
<g
id="g13565"
style="fill:#f2fdff;fill-opacity:0.71171169"
transform="matrix(0.84958,0.276715,-0.703617,0.334119,278.6313,-230.2001)">
<path
id="path13507"
d="m 328.66945,592.8253 -5.97867,10.35298 -5.97867,10.35297 6.18436,0 0,21.24074 11.53226,0 0,-21.24074 6.18435,0 -5.97867,-10.35297 -5.96496,-10.35298 z"
style="fill:#f2fdff;fill-opacity:0.71171169;stroke:none" />
<path
id="path13509"
d="m 328.66945,687.10951 -5.97867,-10.35298 -5.97867,-10.35297 6.18436,0 0,-21.24074 11.53226,0 0,21.24074 6.18435,0 -5.97867,10.35297 -5.96496,10.35298 z"
style="fill:#f2fdff;fill-opacity:0.71171169;stroke:none" />
<path
id="path13511"
d="m 333.74751,639.82449 10.35297,-5.97867 10.35297,-5.97867 0,6.18436 21.24074,0 0,11.53225 -21.24074,0 0,6.18436 -10.35297,-5.97867 -10.35297,-5.96496 z"
style="fill:#f2fdff;fill-opacity:0.71171169;stroke:none" />
<path
id="path13513"
d="m 323.35667,639.82449 -10.35297,-5.97867 -10.35298,-5.97867 0,6.18436 -21.24073,0 0,11.53225 21.24073,0 0,6.18436 10.35298,-5.97867 10.35297,-5.96496 z"
style="fill:#f2fdff;fill-opacity:0.71171169;stroke:none" />
</g>
<rect
style="fill:#f2fdff;fill-opacity:0.70980394;stroke:#000000;stroke-opacity:1"
id="rect6161"
width="91.923882"
height="37.476658"
x="52.679455"
y="60.048466"
transform="translate(33.124945,44.636248)"
rx="5.454824"
ry="5.454824" />
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:Bitstream Charter Bold"
x="91.107697"
y="135.0903"
id="text6673"><tspan
sodipodi:role="line"
id="tspan6675"
x="91.107697"
y="135.0903">MDR</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.8 KiB

BIN
webapp/static/pc.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

158
webapp/static/router.svg Normal file
View file

@ -0,0 +1,158 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="146"
height="100"
id="svg13653"
sodipodi:version="0.32"
inkscape:version="0.47 r22583"
sodipodi:docname="router.svg"
version="1.0">
<defs
id="defs13655">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 99.931252 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="199.10001 : 99.931252 : 1"
inkscape:persp3d-origin="99.550003 : 66.620834 : 1"
id="perspective3835" />
<linearGradient
id="linearGradient12828">
<stop
id="stop12830"
offset="0"
style="stop-color:#484849;stop-opacity:1;" />
<stop
style="stop-color:#434344;stop-opacity:1;"
offset="0"
id="stop12862" />
<stop
id="stop12832"
offset="1.0000000"
style="stop-color:#8f8f90;stop-opacity:0.0000000;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient12828"
id="radialGradient13651"
cx="328.57144"
cy="602.7193"
fx="328.57144"
fy="602.7193"
r="147.14285"
gradientTransform="matrix(1,0,0,0.177184,0,495.9268)"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient12001">
<stop
style="stop-color:#1b4a78;stop-opacity:1;"
offset="0"
id="stop12003" />
<stop
style="stop-color:#5dacd1;stop-opacity:1;"
offset="1"
id="stop12005" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient12001"
id="linearGradient13633"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.471308,0,0,0.471308,118.8781,123.5182)"
x1="175.71875"
y1="737.01562"
x2="470.00089"
y2="737.01562" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient12001"
id="linearGradient3844"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.471308,0,0,0.471308,-45.6934,-239.9103)"
x1="175.71875"
y1="737.01562"
x2="470.00089"
y2="737.01562" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.8"
inkscape:cx="118.57814"
inkscape:cy="50.488033"
inkscape:document-units="px"
inkscape:current-layer="layer1"
inkscape:window-width="1280"
inkscape:window-height="949"
inkscape:window-x="289"
inkscape:window-y="87"
showgrid="false"
inkscape:window-maximized="0" />
<metadata
id="metadata13658">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Capa 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-33.124945,-44.636248)">
<path
style="fill:url(#linearGradient3844);fill-opacity:1;stroke:none"
d="m 37.14136,72.27878 0,0.29457 c 0.006,-0.0975 0.0206,-0.19729 0.0295,-0.29457 l -0.0295,0 z m 138.62351,0 c 0.0302,0.33044 0.0589,0.66821 0.0589,1.00153 l 0,-1.00153 -0.0589,0 z m 0.0589,1.00153 c -1e-5,15.05224 -31.07495,27.26223 -69.35594,27.26223 -37.68286,1e-5 -68.3765,-11.82771 -69.32649,-26.55527 l 0,40.67979 c -0.0151,0.23376 -0.0147,0.45704 -0.0147,0.69223 0,0.22546 8.7e-4,0.45335 0.0147,0.67751 0.91151,14.74102 31.61889,26.59945 69.32649,26.59945 37.7076,0 68.41498,-11.85843 69.32648,-26.59945 l 0.0295,0 0,-0.50077 c 9.5e-4,-0.0587 0,-0.11794 0,-0.17674 0,-0.0588 9.4e-4,-0.11803 0,-0.17674 l 0,-41.90224 z"
id="path13626" />
<path
sodipodi:type="arc"
style="fill:#3a78a0;fill-opacity:1;stroke:none"
id="path11090"
sodipodi:cx="328.57144"
sodipodi:cy="602.7193"
sodipodi:rx="147.14285"
sodipodi:ry="26.071428"
d="m 475.71429,602.7193 c 0,14.39885 -65.87809,26.07143 -147.14285,26.07143 -81.26475,0 -147.14285,-11.67258 -147.14285,-26.07143 0,-14.39885 65.8781,-26.07143 147.14285,-26.07143 81.26476,0 147.14285,11.67258 147.14285,26.07143 z"
transform="matrix(0.471308,0,0,1.045917,-48.3838,-554.9944)" />
<g
id="g13565"
style="fill:#f2fdff;fill-opacity:0.71171169"
transform="matrix(0.84958,0.276715,-0.703617,0.334119,278.6313,-230.2001)">
<path
id="path13507"
d="m 328.66945,592.8253 -5.97867,10.35298 -5.97867,10.35297 6.18436,0 0,21.24074 11.53226,0 0,-21.24074 6.18435,0 -5.97867,-10.35297 -5.96496,-10.35298 z"
style="fill:#f2fdff;fill-opacity:0.71171169;stroke:none" />
<path
id="path13509"
d="m 328.66945,687.10951 -5.97867,-10.35298 -5.97867,-10.35297 6.18436,0 0,-21.24074 11.53226,0 0,21.24074 6.18435,0 -5.97867,10.35297 -5.96496,10.35298 z"
style="fill:#f2fdff;fill-opacity:0.71171169;stroke:none" />
<path
id="path13511"
d="m 333.74751,639.82449 10.35297,-5.97867 10.35297,-5.97867 0,6.18436 21.24074,0 0,11.53225 -21.24074,0 0,6.18436 -10.35297,-5.97867 -10.35297,-5.96496 z"
style="fill:#f2fdff;fill-opacity:0.71171169;stroke:none" />
<path
id="path13513"
d="m 323.35667,639.82449 -10.35297,-5.97867 -10.35298,-5.97867 0,6.18436 -21.24073,0 0,11.53225 21.24073,0 0,6.18436 10.35298,-5.97867 10.35297,-5.96496 z"
style="fill:#f2fdff;fill-opacity:0.71171169;stroke:none" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

File diff suppressed because one or more lines are too long

BIN
webapp/static/wlan.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 B

View file

@ -9,8 +9,11 @@
</head>
<body>
<div class="container-fluid d-flex flex-column p-0">
<nav class="navbar navbar-expand navbar-dark bg-info mb-2">
<span class="navbar-brand mb-0 h1">CORE</span>
<nav class="navbar navbar-expand navbar-dark bg-dark mb-2">
<div class="navbar-brand mb-0 h1">
<img src="static/core-icon.png" />
<span>CORE</span>
</div>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#nb-content"
aria-controls="nb-content" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
@ -51,16 +54,15 @@
</li>
</ul>
<span class="navbar-text border rounded border-white p-2 mr-3">Session #</span>
<span class="navbar-text border rounded border-white p-2">Node: Default</span>
<span id="session-id" class="navbar-text p-2 mr-3">Session #</span>
<span id="node-display" class="navbar-text p-2">Node: Default</span>
</div>
</nav>
<div class="row col">
<div class="col-2 col-xl-1">
<div class="btn-group-vertical w-100" role="group" aria-label="Side Menu">
<button type="button" class="btn btn-secondary">Select</button>
<button type="button" class="btn btn-secondary">Start</button>
<button id="run-button" type="button" class="btn btn-success">Start</button>
<button id="link-button" type="button" class="btn btn-secondary" data-toggle="button">Link Mode</button>
<div class="btn-group dropright node-buttons" role="group">
<button id="menu-nodes" type="button" class="btn btn-secondary dropdown-toggle"
@ -100,25 +102,85 @@
<script src="static/bootstrap.min.js"></script>
<script src="static/vis.min.js"></script>
<script src="static/socket.io.js"></script>
<script src="static/coreip.js"></script>
<script src="static/corerest.js"></script>
<script src="static/corenetwork.js"></script>
<script>
const core = new CoreRest();
core.sessions(function (response) {
console.log('session response: ', response);
});
const coreNetwork = new CoreNetwork('core-network');
const $linkButton = $('#link-button');
const $nodeButtons = $('.node-buttons a');
const $sessionId = $('#session-id');
const $nodeDisplay = $('#node-display');
const $sessionsModel = $('#sessions-modal');
const $sessionsTable = $('#sessions-table');
const $runButton = $('#run-button');
function setRunButton(start) {
const $runButton = $('#run-button');
$runButton.removeClass('btn-danger btn-success');
if (start) {
$runButton.text('Start');
$runButton.addClass('btn-success');
} else {
$runButton.text('Stop');
$runButton.addClass('btn-danger');
}
}
// initial core setup
const coreRest = new CoreRest();
coreRest.retrieveSession()
.then(function (session) {
console.log('get session: ', session);
$sessionId.text(`Session: ${session.id}`);
if (session.state === SessionStates.runtime) {
setRunButton(false);
}
return coreRest.getSession();
})
.then(function(response) {
console.log('session: ', response);
const nodes = response.nodes;
coreNetwork.joinedSessions(nodes);
})
.catch(function (err) {
console.log('get session error: ', err);
});
const coreNetwork = new CoreNetwork('core-network', coreRest);
// page interactions
$linkButton.click(function () {
const linkMode = !$(this).hasClass('active');
coreNetwork.linkMode(linkMode);
$(this).blur();
});
const $nodeButtons = $('.node-buttons a');
$runButton.click(function () {
const $this = $(this);
const start = $this.text() === 'Start';
if (start) {
coreNetwork.start()
.then(function() {
setRunButton(false);
})
.catch(function(err) {
console.log('start error: ', err);
});
} else {
coreRest.shutdownSession()
.then(function (response) {
console.log('shutdown session: ', response);
setRunButton(true);
})
.catch(function (err) {
console.log('shutdown error: ', err);
});
}
});
$nodeDisplay.text(`Node: ${coreNetwork.nodeModel}`);
$nodeButtons.click(function () {
const $this = $(this);
const nodeType = $this.data('node');
@ -126,6 +188,35 @@
console.log('node creation: ', nodeType, model);
console.log('clicked: ', this);
coreNetwork.setNodeMode(nodeType, model);
const currentType = getNodeType(coreNetwork.nodeType);
const defaultType = getNodeType(0);
let nodeDisplay = currentType.display;
if (currentType.display === defaultType.display) {
nodeDisplay = coreNetwork.nodeModel;
}
$nodeDisplay.text(`Node: ${nodeDisplay}`);
});
// show sessions
$sessionsModel.on('shown.bs.modal', function () {
console.log('show sessions');
$sessionsTable.find('tbody tr').remove();
coreRest.getSessions()
.then(function (response) {
const sessions = response.sessions;
for (let session of sessions) {
console.log('show sessions: ', session);
const $idCell = $('<th>', {scope: 'row', text: session.id});
const $nodeCell = $('<td>', {text: session.nodes});
const stateName = coreRest.getStateName(session.state);
const $stateCell = $('<td>', {text: stateName});
const $row = $('<tr>').append([$idCell, $nodeCell, $stateCell]);
$sessionsTable.find('tbody').append($row);
}
})
.catch(function (err) {
console.log('error getting sessions: ', err);
});
});
console.log('connecting to ws');

View file

@ -1,17 +1,27 @@
<div id="sessions-modal" class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal title</h5>
<div class="modal-header bg-dark text-white">
<h5 class="modal-title">Sessions</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>Modal body text goes here.</p>
<table id="sessions-table" class="table table-hover">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Nodes</th>
<th scope="col">State</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary">Save changes</button>
<button type="button" class="btn btn-primary">Join</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>