blob: 481b96b66c4d98bc18380042c6ae692cce1341b2 [file] [log] [blame]
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
ONOS GUI -- Topology Force Module.
Visualization of the topology in an SVG layer, using a D3 Force Layout.
*/
(function () {
'use strict';
// injected refs
var $log,
wss;
// SVG elements;
var linkG,
linkLabelG,
numLinkLblsG,
portLabelG,
nodeG;
// internal state
var settings, // merged default settings and options
force, // force layout object
drag, // drag behavior handler
network = {
nodes: [],
links: [],
linksByDevice: {},
lookup: {},
revLinkToKey: {}
},
lu, // shorthand for lookup
rlk, // shorthand for revLinktoKey
showHosts = false, // whether hosts are displayed
showOffline = true, // whether offline devices are displayed
nodeLock = false, // whether nodes can be dragged or not (locked)
fTimer, // timer for delayed force layout
fNodesTimer, // timer for delayed nodes update
fLinksTimer, // timer for delayed links update
dim, // the dimensions of the force layout [w,h]
linkNums = []; // array of link number labels
// D3 selections;
var link,
linkLabel,
node;
var $log, wss, t2is, t2rs;
// ========================== Helper Functions
function init(_svg_, forceG, _uplink_, _dim_, opts) {
$log.debug('Initialize topo force layout');
nodeG = forceG.append('g').attr('id', 'topo-nodes');
node = nodeG.selectAll('.node');
linkG = forceG.append('g').attr('id', 'topo-links');
linkLabelG = forceG.append('g').attr('id', 'topo-linkLabels');
numLinkLblsG = forceG.append('g').attr('id', 'topo-numLinkLabels');
nodeG = forceG.append('g').attr('id', 'topo-nodes');
portLabelG = forceG.append('g').attr('id', 'topo-portLabels');
link = linkG.selectAll('.link');
linkLabel = linkLabelG.selectAll('.linkLabel');
node = nodeG.selectAll('.node');
var width = 640,
height = 480;
var nodes = [
{ x: width/3, y: height/2 },
{ x: 2*width/3, y: height/2 }
];
var links = [
{ source: 0, target: 1 }
];
var svg = d3.select('body').append('svg')
.attr('width', width)
.attr('height', height);
var force = d3.layout.force()
.size([width, height])
.nodes(nodes)
.links(links);
force.linkDistance(width/2);
var link = svg.selectAll('.link')
.data(links)
.enter().append('line')
.attr('class', 'link');
var node = svg.selectAll('.node')
.data(nodes)
.enter().append('circle')
.attr('class', 'node');
force.start();
}
function destroy() {
$log.debug('Destroy topo force layout');
}
// ========================== Temporary Code (to be deleted later)
function request(dir, rid) {
wss.sendEvent('topo2navRegion', {
dir: dir,
rid: rid
});
}
function doTmpCurrentLayout(data) {
var topdiv = d3.select('#topo2tmp');
var parentRegion = data.parent;
var span = topdiv.select('.parentRegion').select('span');
span.text(parentRegion || '[no parent]');
span.classed('nav-me', !!parentRegion);
}
function doTmpCurrentRegion(data) {
var topdiv = d3.select('#topo2tmp');
var span = topdiv.select('.thisRegion').select('span');
var div;
span.text(data.id);
div = topdiv.select('.subRegions').select('div');
data.subregions.forEach(function (r) {
function nav() {
request('down', r.id);
}
div.append('p')
.classed('nav-me', true)
.text(r.id)
.on('click', nav);
});
div = topdiv.select('.devices').select('div');
data.layerOrder.forEach(function (tag, idx) {
var devs = data.devices[idx];
devs.forEach(function (d) {
div.append('p')
.text('[' + tag + '] ' + d.id);
});
});
div = topdiv.select('.hosts').select('div');
data.layerOrder.forEach(function (tag, idx) {
var hosts = data.hosts[idx];
hosts.forEach(function (h) {
div.append('p')
.text('[' + tag + '] ' + h.id);
});
});
div = topdiv.select('.links').select('div');
var links = data.links;
links.forEach(function (lnk) {
div.append('p')
.text(lnk.id);
});
}
function doTmpPeerRegions(data) {
}
// ========================== Event Handlers
function allInstances(data) {
$log.debug('>> topo2AllInstances event:', data);
doTmpCurrentLayout(data);
t2is.allInstances(data);
}
function currentLayout(data) {
$log.debug('>> topo2CurrentLayout event:', data);
}
function currentRegion(data) {
$log.debug('>> topo2CurrentRegion event:', data);
doTmpCurrentRegion(data);
t2rs.addRegion(data);
}
function topo2PeerRegions(data) {
$log.debug('>> topo2PeerRegions event:', data)
doTmpPeerRegions(data);
}
function topo2PeerRegions(data) {
$log.debug('>> topo2PeerRegions event:', data)
}
function startDone(data) {
$log.debug('>> topo2StartDone event:', data);
}
function showMastership(masterId) {
if (!masterId) {
restoreLayerState();
} else {
showMastershipFor(masterId);
}
}
function restoreLayerState() {
// NOTE: this level of indirection required, for when we have
// the layer filter functionality re-implemented
suppressLayers(false);
}
// ========================== Main Service Definition
function showMastershipFor(id) {
suppressLayers(true);
node.each(function (n) {
if (n.master === id) {
n.el.classed('suppressedmax', false);
}
});
}
function supAmt(less) {
return less ? 'suppressed' : 'suppressedmax';
}
function suppressLayers(b, less) {
var cls = supAmt(less);
node.classed(cls, b);
// link.classed(cls, b);
}
// ========================== Main Service Definition
angular.module('ovTopo2')
.factory('Topo2ForceService',
['$log', 'WebSocketService', 'Topo2InstanceService', 'Topo2RegionService',
function (_$log_, _wss_, _t2is_, _t2rs_) {
$log = _$log_;
wss = _wss_;
t2is = _t2is_;
t2rs = _t2rs_;
return {
init: init,
destroy: destroy,
topo2AllInstances: allInstances,
topo2CurrentLayout: currentLayout,
topo2CurrentRegion: currentRegion,
topo2StartDone: startDone,
showMastership: showMastership,
topo2PeerRegions: topo2PeerRegions
};
}]);
}());