mirror of
https://github.com/opennetworkinglab/onos.git
synced 2025-10-18 19:01:06 +02:00
Updated fn-spec to include classNames
Removed Classnames file and added code to fn.js Fixed typo dimentions to dimensions Moved Device/Link logic from Topo2D3 into the model Model now calls onChange when any property is changed via the set Method WIP - Added d3 force layout for devices and lines Change-Id: I4d1afd3cd4cecf2f719e27f4be5d1e874bd9e342
This commit is contained in:
parent
ee5d121524
commit
ec1f45c00c
@ -386,6 +386,34 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var hasOwn = {}.hasOwnProperty;
|
||||||
|
|
||||||
|
function classNames () {
|
||||||
|
var classes = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < arguments.length; i++) {
|
||||||
|
var arg = arguments[i];
|
||||||
|
if (!arg) continue;
|
||||||
|
|
||||||
|
var argType = typeof arg;
|
||||||
|
|
||||||
|
if (argType === 'string' || argType === 'number') {
|
||||||
|
classes.push(arg);
|
||||||
|
} else if (Array.isArray(arg)) {
|
||||||
|
classes.push(classNames.apply(null, arg));
|
||||||
|
} else if (argType === 'object') {
|
||||||
|
for (var key in arg) {
|
||||||
|
if (hasOwn.call(arg, key) && arg[key]) {
|
||||||
|
classes.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return classes.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
angular.module('onosUtil')
|
angular.module('onosUtil')
|
||||||
.factory('FnService',
|
.factory('FnService',
|
||||||
['$window', '$location', '$log', function (_$window_, $loc, _$log_) {
|
['$window', '$location', '$log', function (_$window_, $loc, _$log_) {
|
||||||
@ -423,7 +451,8 @@
|
|||||||
parseBitRate: parseBitRate,
|
parseBitRate: parseBitRate,
|
||||||
addToTrie: addToTrie,
|
addToTrie: addToTrie,
|
||||||
removeFromTrie: removeFromTrie,
|
removeFromTrie: removeFromTrie,
|
||||||
trieLookup: trieLookup
|
trieLookup: trieLookup,
|
||||||
|
classNames: classNames
|
||||||
};
|
};
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
|
@ -320,6 +320,7 @@
|
|||||||
// updateLinks - subfunctions
|
// updateLinks - subfunctions
|
||||||
|
|
||||||
function linkEntering(d) {
|
function linkEntering(d) {
|
||||||
|
|
||||||
var link = d3.select(this);
|
var link = d3.select(this);
|
||||||
d.el = link;
|
d.el = link;
|
||||||
api.restyleLinkElement(d);
|
api.restyleLinkElement(d);
|
||||||
|
@ -103,6 +103,7 @@
|
|||||||
// === EVENT HANDLERS
|
// === EVENT HANDLERS
|
||||||
|
|
||||||
function addDevice(data) {
|
function addDevice(data) {
|
||||||
|
console.log(data);
|
||||||
var id = data.id,
|
var id = data.id,
|
||||||
d;
|
d;
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
ONOS GUI -- Topology View (theme) -- CSS file
|
ONOS GUI -- Topology View (theme) -- CSS file
|
||||||
*/
|
*/
|
||||||
@ -22,8 +21,7 @@
|
|||||||
/* --- Base SVG Layer --- */
|
/* --- Base SVG Layer --- */
|
||||||
|
|
||||||
#ov-topo2 svg {
|
#ov-topo2 svg {
|
||||||
/*background-color: #f4f4f4;*/
|
background-color: #f4f4f4;
|
||||||
background-color: goldenrod; /* just for testing */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- "No Devices" Layer --- */
|
/* --- "No Devices" Layer --- */
|
||||||
@ -32,15 +30,355 @@
|
|||||||
fill: #db7773;
|
fill: #db7773;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ov-topo2 svg #topo2-noDevsLayer text {
|
#ov-topo2 svg #topo-noDevsLayer text {
|
||||||
fill: #7e9aa8;
|
fill: #7e9aa8;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- Topo Map --- */
|
/* --- Topo Map --- */
|
||||||
|
|
||||||
#ov-topo2 svg #topo2-map {
|
#ov-topo2 svg #topo-map {
|
||||||
stroke-width: 2px;
|
stroke-width: 2px;
|
||||||
stroke: #f4f4f4;
|
stroke: #f4f4f4;
|
||||||
fill: #e5e5e6;
|
fill: #e5e5e6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --- general topo-panel styling --- */
|
||||||
|
|
||||||
|
.topo-p svg {
|
||||||
|
background: #c0242b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topo-p svg .glyph {
|
||||||
|
fill: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topo-p hr {
|
||||||
|
background-color: #cccccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
#topo-p-detail svg {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#topo-p-detail .header svg .glyph {
|
||||||
|
fill: #c0242b;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* --- Topo Instance Panel --- */
|
||||||
|
|
||||||
|
#topo-p-instance svg rect {
|
||||||
|
stroke-width: 0;
|
||||||
|
fill: #fbfbfb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* body of an instance */
|
||||||
|
#topo-p-instance .online svg rect {
|
||||||
|
opacity: 1;
|
||||||
|
fill: #fbfbfb;
|
||||||
|
}
|
||||||
|
|
||||||
|
#topo-p-instance svg .glyph {
|
||||||
|
fill: #fff;
|
||||||
|
}
|
||||||
|
#topo-p-instance .online svg .glyph {
|
||||||
|
fill: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* offline */
|
||||||
|
#topo-p-instance svg .badgeIcon {
|
||||||
|
opacity: 0.4;
|
||||||
|
fill: #939598;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* online */
|
||||||
|
#topo-p-instance .online svg .badgeIcon {
|
||||||
|
opacity: 1.0;
|
||||||
|
fill: #939598;
|
||||||
|
}
|
||||||
|
#topo-p-instance .online svg .badgeIcon.bird {
|
||||||
|
fill: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#topo-p-instance svg .readyBadge {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
#topo-p-instance .ready svg .readyBadge {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
#topo-p-instance svg text {
|
||||||
|
text-anchor: left;
|
||||||
|
opacity: 0.5;
|
||||||
|
fill: #3c3a3a;
|
||||||
|
}
|
||||||
|
|
||||||
|
#topo-p-instance .online svg text {
|
||||||
|
opacity: 1.0;
|
||||||
|
fill: #3c3a3a;
|
||||||
|
}
|
||||||
|
|
||||||
|
#topo-p-instance .onosInst.mastership {
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
#topo-p-instance .onosInst.mastership.affinity {
|
||||||
|
opacity: 1.0;
|
||||||
|
}
|
||||||
|
#topo-p-instance .onosInst.mastership.affinity svg rect {
|
||||||
|
filter: url(#blue-glow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.firefox #topo-p-instance .onosInst.mastership.affinity svg rect {
|
||||||
|
filter: url("data:image/svg+xml;utf8, <svg xmlns = \'http://www.w3.org/2000/svg\'><filter x=\"-50%\" y=\"-50%\" width=\"200%\" height=\"200%\" id=\"blue-glow\"><feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.7 0 0 0 1 0 \"></feColorMatrix><feGaussianBlur stdDeviation=\"3\" result=\"coloredBlur\"></feGaussianBlur><feMerge><feMergeNode in=\"coloredBlur\"></feMergeNode><feMergeNode in=\"SourceGraphic\"></feMergeNode></feMerge></filter></svg>#blue-glow");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Topo Nodes --- */
|
||||||
|
|
||||||
|
#ov-topo2 svg .suppressed {
|
||||||
|
opacity: 0.5 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ov-topo2 svg .suppressedmax {
|
||||||
|
opacity: 0.2 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Device Nodes */
|
||||||
|
|
||||||
|
/* note: device without the 'online' class is offline */
|
||||||
|
#ov-topo2 svg .node.device rect {
|
||||||
|
/* TODO: theme */
|
||||||
|
fill: #f0f0f0;
|
||||||
|
}
|
||||||
|
#ov-topo2 svg .node.device text {
|
||||||
|
/*TODO: theme*/
|
||||||
|
fill: #bbb;
|
||||||
|
}
|
||||||
|
#ov-topo2 svg .node.device use {
|
||||||
|
/*TODO: theme*/
|
||||||
|
fill: #777;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ov-topo2 svg .node.device.online rect {
|
||||||
|
fill: #ffffff;
|
||||||
|
}
|
||||||
|
#ov-topo2 svg .node.device.online text {
|
||||||
|
fill: #3c3a3a;
|
||||||
|
}
|
||||||
|
#ov-topo2 svg .node.device.online use {
|
||||||
|
/* NOTE: this gets overridden programatically */
|
||||||
|
fill: #454545;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ov-topo2 svg .node.device.selected rect {
|
||||||
|
stroke-width: 2.0;
|
||||||
|
stroke: #009fdb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Badges */
|
||||||
|
/* (... works for bothand dark themes...) */
|
||||||
|
#ov-topo2 svg .node .badge circle {
|
||||||
|
stroke: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ov-topo2 svg .node .badge.badgeInfo circle {
|
||||||
|
fill: #99d;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ov-topo2 svg .node .badge.badgeWarn circle {
|
||||||
|
fill: #da2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ov-topo2 svg .node .badge.badgeError circle {
|
||||||
|
fill: #e44;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ov-topo2 svg .node .badge use {
|
||||||
|
fill: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ov-topo2 svg .node .badge.badgeInfo use {
|
||||||
|
fill: #448;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ov-topo2 svg .node .badge text {
|
||||||
|
fill: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ov-topo2 svg .node .badge.badgeInfo text {
|
||||||
|
fill: #448;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Host Nodes */
|
||||||
|
|
||||||
|
#ov-topo2 svg .node.host {
|
||||||
|
}
|
||||||
|
|
||||||
|
#ov-topo2 svg .node.host text {
|
||||||
|
stroke: none;
|
||||||
|
font: 9pt sans-serif;
|
||||||
|
fill: #846;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ov-topo2 svg .node.host circle {
|
||||||
|
stroke: #a3a596;
|
||||||
|
fill: #e0dfd6;
|
||||||
|
}
|
||||||
|
#ov-topo2 svg .node.host.selected .hostIcon > circle {
|
||||||
|
stroke-width: 2.0;
|
||||||
|
stroke: #009fdb;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ov-topo2 svg .node.host use {
|
||||||
|
fill: #3c3a3a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Topo Links --- */
|
||||||
|
|
||||||
|
#ov-topo2 svg .link {
|
||||||
|
opacity: .9;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ov-topo2 svg .link.selected,
|
||||||
|
#ov-topo2 svg .link.enhanced {
|
||||||
|
stroke-width: 3.5;
|
||||||
|
stroke: #009fdb;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ov-topo2 svg .link.inactive {
|
||||||
|
opacity: .5;
|
||||||
|
stroke-dasharray: 8 4;
|
||||||
|
}
|
||||||
|
/* TODO: Review for not-permitted links */
|
||||||
|
#ov-topo2 svg .link.not-permitted {
|
||||||
|
stroke: rgb(255,0,0);
|
||||||
|
stroke-width: 5.0;
|
||||||
|
stroke-dasharray: 8 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ov-topo2 svg .link.secondary {
|
||||||
|
stroke-width: 3px;
|
||||||
|
stroke: rgba(0,153,51,0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Port traffic color visualization for Kbps, Mbps, and Gbps */
|
||||||
|
|
||||||
|
#ov-topo2 svg .link.secondary.port-traffic-Kbps {
|
||||||
|
stroke: rgb(0,153,51);
|
||||||
|
stroke-width: 5.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ov-topo2 svg .link.secondary.port-traffic-Mbps {
|
||||||
|
stroke: rgb(128,145,27);
|
||||||
|
stroke-width: 6.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ov-topo2 svg .link.secondary.port-traffic-Gbps {
|
||||||
|
stroke: rgb(255, 137, 3);
|
||||||
|
stroke-width: 8.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ov-topo2 svg .link.secondary.port-traffic-Gbps-choked {
|
||||||
|
stroke: rgb(183, 30, 21);
|
||||||
|
stroke-width: 8.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ov-topo2 svg .link.animated {
|
||||||
|
stroke-dasharray: 8 5;
|
||||||
|
animation: ants 5s infinite linear;
|
||||||
|
/* below line could be added via Javascript, based on path, if we cared
|
||||||
|
* enough about the direction of ant-flow
|
||||||
|
*/
|
||||||
|
/*animation-direction: reverse;*/
|
||||||
|
}
|
||||||
|
@keyframes ants {
|
||||||
|
from {
|
||||||
|
stroke-dashoffset: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
stroke-dashoffset: 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ov-topo2 svg .link.primary {
|
||||||
|
stroke-width: 4px;
|
||||||
|
stroke: #ffA300;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ov-topo2 svg .link.secondary.optical {
|
||||||
|
stroke-width: 4px;
|
||||||
|
stroke: rgba(128,64,255,0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ov-topo2 svg .link.primary.optical {
|
||||||
|
stroke-width: 6px;
|
||||||
|
stroke: #74f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Link Labels */
|
||||||
|
#ov-topo2 svg .linkLabel rect {
|
||||||
|
stroke: none;
|
||||||
|
fill: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ov-topo2 svg .linkLabel text {
|
||||||
|
fill: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Port Labels */
|
||||||
|
|
||||||
|
#ov-topo2 svg .portLabel rect {
|
||||||
|
stroke: #a3a596;
|
||||||
|
fill: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ov-topo2 svg .portLabel text {
|
||||||
|
fill: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Number of Links Labels */
|
||||||
|
|
||||||
|
|
||||||
|
#ov-topo2 text.numLinkText {
|
||||||
|
fill: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------- */
|
||||||
|
/* Sprite Layer */
|
||||||
|
|
||||||
|
#ov-topo2 svg #topo-sprites .gold1 use {
|
||||||
|
stroke: #fda;
|
||||||
|
fill: none;
|
||||||
|
}
|
||||||
|
#ov-topo2 svg #topo-sprites .gold1 text {
|
||||||
|
fill: #eda;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ov-topo2 svg #topo-sprites .blue1 use {
|
||||||
|
stroke: #bbd;
|
||||||
|
fill: none;
|
||||||
|
}
|
||||||
|
#ov-topo2 svg #topo-sprites .blue1 text {
|
||||||
|
fill: #cce;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ov-topo2 svg #topo-sprites .gray1 use {
|
||||||
|
stroke: #ccc;
|
||||||
|
fill: none;
|
||||||
|
}
|
||||||
|
#ov-topo2 svg #topo-sprites .gray1 text {
|
||||||
|
fill: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fills */
|
||||||
|
#ov-topo2 svg #topo-sprites use.fill-gray2 {
|
||||||
|
fill: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ov-topo2 svg #topo-sprites use.fill-blue2 {
|
||||||
|
fill: #bce;
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<!-- Topology View partial HTML -->
|
<!-- Topology View partial HTML -->
|
||||||
<div id="ov-topo2">
|
<div id="ov-topo2">
|
||||||
<div id="topo2tmp">
|
|
||||||
|
<!-- <div id="topo2tmp">
|
||||||
<div class="parentRegion">
|
<div class="parentRegion">
|
||||||
Parent Region: <span> - </span>
|
Parent Region: <span> - </span>
|
||||||
</div>
|
</div>
|
||||||
@ -27,7 +28,7 @@
|
|||||||
<h4>Peers</h4>
|
<h4>Peers</h4>
|
||||||
<div></div>
|
<div></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
<!-- Below here is good; Above here is temporary, for debugging -->
|
<!-- Below here is good; Above here is temporary, for debugging -->
|
||||||
|
|
||||||
|
@ -48,6 +48,7 @@
|
|||||||
// callback invoked when the SVG view has been resized..
|
// callback invoked when the SVG view has been resized..
|
||||||
function svgResized(s) {
|
function svgResized(s) {
|
||||||
$log.debug('topo2 view resized', s);
|
$log.debug('topo2 view resized', s);
|
||||||
|
t2fs.newDim([s.width, s.height]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setUpKeys(overlayKeys) {
|
function setUpKeys(overlayKeys) {
|
||||||
@ -68,7 +69,7 @@
|
|||||||
ps.setPrefs('topo_zoom', {tx:tr[0], ty:tr[1], sc:sc});
|
ps.setPrefs('topo_zoom', {tx:tr[0], ty:tr[1], sc:sc});
|
||||||
|
|
||||||
// keep the map lines constant width while zooming
|
// keep the map lines constant width while zooming
|
||||||
mapG.style('stroke-width', (2.0 / sc) + 'px');
|
// mapG.style('stroke-width', (2.0 / sc) + 'px');
|
||||||
}
|
}
|
||||||
|
|
||||||
function setUpZoom() {
|
function setUpZoom() {
|
||||||
|
@ -55,8 +55,6 @@
|
|||||||
_this._byId[d.id] = model;
|
_this._byId[d.id] = model;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// this.sort();
|
|
||||||
},
|
},
|
||||||
get: function (id) {
|
get: function (id) {
|
||||||
if (!id) {
|
if (!id) {
|
||||||
@ -77,7 +75,10 @@
|
|||||||
_reset: function () {
|
_reset: function () {
|
||||||
this._byId = [];
|
this._byId = [];
|
||||||
this.models = [];
|
this.models = [];
|
||||||
}
|
},
|
||||||
|
toJSON: function(options) {
|
||||||
|
return this.models.map(function(model) { return model.toJSON(options); });
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Collection.extend = function (protoProps, staticProps) {
|
Collection.extend = function (protoProps, staticProps) {
|
||||||
|
163
web/gui/src/main/webapp/app/view/topo2/topo2D3.js
Normal file
163
web/gui/src/main/webapp/app/view/topo2/topo2D3.js
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
/*
|
||||||
|
* 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 Layout Module.
|
||||||
|
Module that contains the d3.force.layout logic
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var sus, is, ts;
|
||||||
|
|
||||||
|
// internal state
|
||||||
|
var deviceLabelIndex = 0,
|
||||||
|
hostLabelIndex = 0;
|
||||||
|
|
||||||
|
// configuration
|
||||||
|
var devIconDim = 36,
|
||||||
|
labelPad = 4,
|
||||||
|
hostRadius = 14,
|
||||||
|
badgeConfig = {
|
||||||
|
radius: 12,
|
||||||
|
yoff: 5,
|
||||||
|
gdelta: 10
|
||||||
|
},
|
||||||
|
halfDevIcon = devIconDim / 2,
|
||||||
|
devBadgeOff = { dx: -halfDevIcon, dy: -halfDevIcon },
|
||||||
|
hostBadgeOff = { dx: -hostRadius, dy: -hostRadius },
|
||||||
|
status = {
|
||||||
|
i: 'badgeInfo',
|
||||||
|
w: 'badgeWarn',
|
||||||
|
e: 'badgeError'
|
||||||
|
};
|
||||||
|
|
||||||
|
// note: these are the device icon colors without affinity (no master)
|
||||||
|
var dColTheme = {
|
||||||
|
light: {
|
||||||
|
online: '#444444',
|
||||||
|
offline: '#cccccc'
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
|
// TODO: theme
|
||||||
|
online: '#444444',
|
||||||
|
offline: '#cccccc'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function init() {}
|
||||||
|
|
||||||
|
function renderBadge(node, bdg, boff) {
|
||||||
|
var bsel,
|
||||||
|
bcr = badgeConfig.radius,
|
||||||
|
bcgd = badgeConfig.gdelta;
|
||||||
|
|
||||||
|
node.select('g.badge').remove();
|
||||||
|
|
||||||
|
bsel = node.append('g')
|
||||||
|
.classed('badge', true)
|
||||||
|
.classed(badgeStatus(bdg), true)
|
||||||
|
.attr('transform', sus.translate(boff.dx, boff.dy));
|
||||||
|
|
||||||
|
bsel.append('circle')
|
||||||
|
.attr('r', bcr);
|
||||||
|
|
||||||
|
if (bdg.txt) {
|
||||||
|
bsel.append('text')
|
||||||
|
.attr('dy', badgeConfig.yoff)
|
||||||
|
.attr('text-anchor', 'middle')
|
||||||
|
.text(bdg.txt);
|
||||||
|
} else if (bdg.gid) {
|
||||||
|
bsel.append('use')
|
||||||
|
.attr({
|
||||||
|
width: bcgd * 2,
|
||||||
|
height: bcgd * 2,
|
||||||
|
transform: sus.translate(-bcgd, -bcgd),
|
||||||
|
'xlink:href': '#' + bdg.gid
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Move to Device Model when working on the Exit Devices
|
||||||
|
function updateDeviceRendering(d) {
|
||||||
|
var node = d.el,
|
||||||
|
bdg = d.badge,
|
||||||
|
label = trimLabel(deviceLabel(d)),
|
||||||
|
labelWidth;
|
||||||
|
|
||||||
|
node.select('text').text(label);
|
||||||
|
labelWidth = label ? computeLabelWidth(node) : 0;
|
||||||
|
|
||||||
|
node.select('rect')
|
||||||
|
.transition()
|
||||||
|
.attr(iconBox(devIconDim, labelWidth));
|
||||||
|
|
||||||
|
if (bdg) {
|
||||||
|
renderBadge(node, bdg, devBadgeOff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function deviceEnter(device) {
|
||||||
|
device.onEnter(this, device);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hostLabel(d) {
|
||||||
|
return d.get('id');
|
||||||
|
|
||||||
|
// var idx = (hostLabelIndex < d.get('labels').length) ? hostLabelIndex : 0;
|
||||||
|
// return d.labels[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
function hostEnter(d) {
|
||||||
|
var node = d3.select(this),
|
||||||
|
gid = d.get('type') || 'unknown',
|
||||||
|
textDy = hostRadius + 10;
|
||||||
|
|
||||||
|
d.el = node;
|
||||||
|
// sus.visible(node, api.showHosts());
|
||||||
|
|
||||||
|
is.addHostIcon(node, hostRadius, gid);
|
||||||
|
|
||||||
|
node.append('text')
|
||||||
|
.text(hostLabel)
|
||||||
|
.attr('dy', textDy)
|
||||||
|
.attr('text-anchor', 'middle');
|
||||||
|
}
|
||||||
|
|
||||||
|
function linkEntering(link) {
|
||||||
|
link.onEnter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
angular.module('ovTopo2')
|
||||||
|
.factory('Topo2D3Service',
|
||||||
|
['SvgUtilService', 'IconService', 'ThemeService',
|
||||||
|
|
||||||
|
function (_sus_, _is_, _ts_) {
|
||||||
|
sus = _sus_;
|
||||||
|
is = _is_;
|
||||||
|
ts = _ts_;
|
||||||
|
|
||||||
|
return {
|
||||||
|
init: init,
|
||||||
|
deviceEnter: deviceEnter,
|
||||||
|
hostEnter: hostEnter,
|
||||||
|
linkEntering: linkEntering
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
);
|
||||||
|
})();
|
@ -22,16 +22,37 @@
|
|||||||
(function () {
|
(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var Collection, Model;
|
var Collection, Model, is, sus, ts, t2vs;
|
||||||
|
|
||||||
|
var remappedDeviceTypes = {
|
||||||
|
virtual: 'cord'
|
||||||
|
};
|
||||||
|
|
||||||
|
// configuration
|
||||||
|
var devIconDim = 36,
|
||||||
|
labelPad = 10,
|
||||||
|
hostRadius = 14,
|
||||||
|
badgeConfig = {
|
||||||
|
radius: 12,
|
||||||
|
yoff: 5,
|
||||||
|
gdelta: 10
|
||||||
|
},
|
||||||
|
halfDevIcon = devIconDim / 2,
|
||||||
|
devBadgeOff = { dx: -halfDevIcon, dy: -halfDevIcon },
|
||||||
|
hostBadgeOff = { dx: -hostRadius, dy: -hostRadius },
|
||||||
|
status = {
|
||||||
|
i: 'badgeInfo',
|
||||||
|
w: 'badgeWarn',
|
||||||
|
e: 'badgeError'
|
||||||
|
},
|
||||||
|
deviceLabelIndex = 0;
|
||||||
|
|
||||||
function createDeviceCollection(data, region) {
|
function createDeviceCollection(data, region) {
|
||||||
|
|
||||||
var DeviceCollection = Collection.extend({
|
var DeviceCollection = Collection.extend({
|
||||||
model: Model,
|
model: Model,
|
||||||
get: function () {},
|
|
||||||
comparator: function(a, b) {
|
comparator: function(a, b) {
|
||||||
|
var order = region.get('layerOrder');
|
||||||
var order = region.layerOrder;
|
|
||||||
return order.indexOf(a.get('layer')) - order.indexOf(b.get('layer'));
|
return order.indexOf(a.get('layer')) - order.indexOf(b.get('layer'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -49,14 +70,106 @@
|
|||||||
return deviceCollection;
|
return deviceCollection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mapDeviceTypeToGlyph(type) {
|
||||||
|
return remappedDeviceTypes[type] || type || 'unknown';
|
||||||
|
}
|
||||||
|
|
||||||
|
function deviceLabel(d) {
|
||||||
|
//TODO: Device Json is missing labels array
|
||||||
|
return "";
|
||||||
|
var labels = this.get('labels'),
|
||||||
|
idx = (deviceLabelIndex < labels.length) ? deviceLabelIndex : 0;
|
||||||
|
return labels[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
function trimLabel(label) {
|
||||||
|
return (label && label.trim()) || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function computeLabelWidth() {
|
||||||
|
var text = this.select('text'),
|
||||||
|
box = text.node().getBBox();
|
||||||
|
return box.width + labelPad * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function iconBox(dim, labelWidth) {
|
||||||
|
return {
|
||||||
|
x: -dim / 2,
|
||||||
|
y: -dim / 2,
|
||||||
|
width: dim + labelWidth,
|
||||||
|
height: dim
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function deviceGlyphColor(d) {
|
||||||
|
|
||||||
|
var o = this.node.online,
|
||||||
|
id = "127.0.0.1", // TODO: This should be from node.master
|
||||||
|
otag = o ? 'online' : 'offline';
|
||||||
|
return o ? sus.cat7().getColor(id, 0, ts.theme())
|
||||||
|
: dColTheme[ts.theme()][otag];
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDeviceColor() {
|
||||||
|
this.el.select('use')
|
||||||
|
.style('fill', this.deviceGlyphColor());
|
||||||
|
}
|
||||||
|
|
||||||
angular.module('ovTopo2')
|
angular.module('ovTopo2')
|
||||||
.factory('Topo2DeviceService',
|
.factory('Topo2DeviceService',
|
||||||
['Topo2Collection', 'Topo2Model',
|
['Topo2Collection', 'Topo2NodeModel', 'IconService', 'SvgUtilService',
|
||||||
|
'ThemeService', 'Topo2ViewService',
|
||||||
|
|
||||||
function (_Collection_, _Model_) {
|
function (_Collection_, _NodeModel_, _is_, _sus_, _ts_, classnames, _t2vs_) {
|
||||||
|
|
||||||
|
t2vs = _t2vs_;
|
||||||
|
is = _is_;
|
||||||
|
sus = _sus_;
|
||||||
|
ts = _ts_;
|
||||||
Collection = _Collection_;
|
Collection = _Collection_;
|
||||||
Model = _Model_.extend({});
|
|
||||||
|
Model = _NodeModel_.extend({
|
||||||
|
initialize: function () {
|
||||||
|
this.set('weight', 0);
|
||||||
|
this.constructor.__super__.initialize.apply(this, arguments);
|
||||||
|
},
|
||||||
|
nodeType: 'device',
|
||||||
|
deviceLabel: deviceLabel,
|
||||||
|
deviceGlyphColor: deviceGlyphColor,
|
||||||
|
mapDeviceTypeToGlyph: mapDeviceTypeToGlyph,
|
||||||
|
trimLabel: trimLabel,
|
||||||
|
setDeviceColor: setDeviceColor,
|
||||||
|
onEnter: function (el) {
|
||||||
|
|
||||||
|
var node = d3.select(el),
|
||||||
|
glyphId = mapDeviceTypeToGlyph(this.get('type')),
|
||||||
|
label = trimLabel(this.deviceLabel()),
|
||||||
|
rect, text, glyph, labelWidth;
|
||||||
|
|
||||||
|
this.el = node;
|
||||||
|
|
||||||
|
rect = node.append('rect');
|
||||||
|
|
||||||
|
text = node.append('text').text(label)
|
||||||
|
.attr('text-anchor', 'left')
|
||||||
|
.attr('y', '0.3em')
|
||||||
|
.attr('x', halfDevIcon + labelPad);
|
||||||
|
|
||||||
|
glyph = is.addDeviceIcon(node, glyphId, devIconDim);
|
||||||
|
|
||||||
|
labelWidth = label ? computeLabelWidth(node) : 0;
|
||||||
|
|
||||||
|
rect.attr(iconBox(devIconDim, labelWidth));
|
||||||
|
glyph.attr(iconBox(devIconDim, 0));
|
||||||
|
|
||||||
|
node.attr('transform', sus.translate(-halfDevIcon, -halfDevIcon));
|
||||||
|
this.render();
|
||||||
|
},
|
||||||
|
onExit: function () {},
|
||||||
|
render: function () {
|
||||||
|
this.setDeviceColor();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
createDeviceCollection: createDeviceCollection
|
createDeviceCollection: createDeviceCollection
|
||||||
|
@ -60,62 +60,17 @@
|
|||||||
linkLabel,
|
linkLabel,
|
||||||
node;
|
node;
|
||||||
|
|
||||||
var $log, wss, t2is, t2rs;
|
var $log, wss, t2is, t2rs, t2ls, t2vs;
|
||||||
|
var svg, forceG, uplink, dim, opts;
|
||||||
|
|
||||||
// ========================== Helper Functions
|
// ========================== Helper Functions
|
||||||
|
|
||||||
function init(_svg_, forceG, _uplink_, _dim_, opts) {
|
function init(_svg_, _forceG_, _uplink_, _dim_, _opts_) {
|
||||||
|
svg = _svg_;
|
||||||
$log.debug('Initialize topo force layout');
|
forceG = _forceG_;
|
||||||
|
uplink = _uplink_;
|
||||||
nodeG = forceG.append('g').attr('id', 'topo-nodes');
|
dim = _dim_;
|
||||||
node = nodeG.selectAll('.node');
|
opts = _opts_
|
||||||
|
|
||||||
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() {
|
function destroy() {
|
||||||
@ -206,6 +161,9 @@
|
|||||||
$log.debug('>> topo2CurrentRegion event:', data);
|
$log.debug('>> topo2CurrentRegion event:', data);
|
||||||
doTmpCurrentRegion(data);
|
doTmpCurrentRegion(data);
|
||||||
t2rs.addRegion(data);
|
t2rs.addRegion(data);
|
||||||
|
t2ls.init(svg, forceG, uplink, dim, opts);
|
||||||
|
t2ls.update();
|
||||||
|
t2ls.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
function topo2PeerRegions(data) {
|
function topo2PeerRegions(data) {
|
||||||
@ -257,20 +215,37 @@
|
|||||||
// link.classed(cls, b);
|
// link.classed(cls, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function newDim(_dim_) {
|
||||||
|
dim = _dim_;
|
||||||
|
t2vs.newDim(dim);
|
||||||
|
// force.size(dim);
|
||||||
|
// tms.newDim(dim);
|
||||||
|
t2ls.setDimensions();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDim() {
|
||||||
|
return dim;
|
||||||
|
}
|
||||||
|
|
||||||
// ========================== Main Service Definition
|
// ========================== Main Service Definition
|
||||||
|
|
||||||
angular.module('ovTopo2')
|
angular.module('ovTopo2')
|
||||||
.factory('Topo2ForceService',
|
.factory('Topo2ForceService',
|
||||||
['$log', 'WebSocketService', 'Topo2InstanceService', 'Topo2RegionService',
|
['$log', 'WebSocketService', 'Topo2InstanceService', 'Topo2RegionService',
|
||||||
function (_$log_, _wss_, _t2is_, _t2rs_) {
|
'Topo2LayoutService', 'Topo2ViewService',
|
||||||
|
function (_$log_, _wss_, _t2is_, _t2rs_, _t2ls_, _t2vs_) {
|
||||||
|
|
||||||
$log = _$log_;
|
$log = _$log_;
|
||||||
wss = _wss_;
|
wss = _wss_;
|
||||||
t2is = _t2is_;
|
t2is = _t2is_;
|
||||||
t2rs = _t2rs_;
|
t2rs = _t2rs_;
|
||||||
|
t2ls = _t2ls_;
|
||||||
|
t2vs = _t2vs_;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
||||||
init: init,
|
init: init,
|
||||||
|
newDim: newDim,
|
||||||
|
|
||||||
destroy: destroy,
|
destroy: destroy,
|
||||||
topo2AllInstances: allInstances,
|
topo2AllInstances: allInstances,
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
(function () {
|
(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var Collection, Model;
|
var Collection, Model, t2vs;
|
||||||
|
|
||||||
function createHostCollection(data, region) {
|
function createHostCollection(data, region) {
|
||||||
|
|
||||||
@ -42,12 +42,16 @@
|
|||||||
|
|
||||||
angular.module('ovTopo2')
|
angular.module('ovTopo2')
|
||||||
.factory('Topo2HostService',
|
.factory('Topo2HostService',
|
||||||
['Topo2Collection', 'Topo2Model',
|
[
|
||||||
|
'Topo2Collection', 'Topo2NodeModel', 'Topo2ViewService',
|
||||||
function (_Collection_, _Model_) {
|
function (_Collection_, _NodeModel_, classnames, _t2vs_) {
|
||||||
|
|
||||||
|
t2vs = _t2vs_;
|
||||||
Collection = _Collection_;
|
Collection = _Collection_;
|
||||||
Model = _Model_.extend();
|
|
||||||
|
Model = _NodeModel_.extend({
|
||||||
|
nodeType: 'host'
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
createHostCollection: createHostCollection
|
createHostCollection: createHostCollection
|
||||||
|
334
web/gui/src/main/webapp/app/view/topo2/topo2Layout.js
Normal file
334
web/gui/src/main/webapp/app/view/topo2/topo2Layout.js
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
/*
|
||||||
|
* 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 Layout Module.
|
||||||
|
Module that contains the d3.force.layout logic
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var $log, sus, t2rs, t2d3, t2vs;
|
||||||
|
|
||||||
|
var linkG, linkLabelG, numLinkLabelsG, nodeG, portLabelG;
|
||||||
|
var link, linkLabel, node;
|
||||||
|
|
||||||
|
var nodes, links;
|
||||||
|
|
||||||
|
var force;
|
||||||
|
|
||||||
|
// default settings for force layout
|
||||||
|
var defaultSettings = {
|
||||||
|
gravity: 0.4,
|
||||||
|
friction: 0.7,
|
||||||
|
charge: {
|
||||||
|
// note: key is node.class
|
||||||
|
device: -8000,
|
||||||
|
host: -5000,
|
||||||
|
_def_: -12000
|
||||||
|
},
|
||||||
|
linkDistance: {
|
||||||
|
// note: key is link.type
|
||||||
|
direct: 100,
|
||||||
|
optical: 120,
|
||||||
|
hostLink: 3,
|
||||||
|
_def_: 50
|
||||||
|
},
|
||||||
|
linkStrength: {
|
||||||
|
// note: key is link.type
|
||||||
|
// range: {0.0 ... 1.0}
|
||||||
|
//direct: 1.0,
|
||||||
|
//optical: 1.0,
|
||||||
|
//hostLink: 1.0,
|
||||||
|
_def_: 1.0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// configuration
|
||||||
|
var linkConfig = {
|
||||||
|
light: {
|
||||||
|
baseColor: '#939598',
|
||||||
|
inColor: '#66f',
|
||||||
|
outColor: '#f00'
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
|
// TODO : theme
|
||||||
|
baseColor: '#939598',
|
||||||
|
inColor: '#66f',
|
||||||
|
outColor: '#f00'
|
||||||
|
},
|
||||||
|
inWidth: 12,
|
||||||
|
outWidth: 10
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
var tickStuff = {
|
||||||
|
nodeAttr: {
|
||||||
|
transform: function (d) {
|
||||||
|
var dx = isNaN(d.x) ? 0 : d.x,
|
||||||
|
dy = isNaN(d.y) ? 0 : d.y;
|
||||||
|
return sus.translate(dx, dy);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
linkAttr: {
|
||||||
|
x1: function (d) { return d.get('position').x1; },
|
||||||
|
y1: function (d) { return d.get('position').y1; },
|
||||||
|
x2: function (d) { return d.get('position').x2; },
|
||||||
|
y2: function (d) { return d.get('position').y2; }
|
||||||
|
},
|
||||||
|
linkLabelAttr: {
|
||||||
|
transform: function (d) {
|
||||||
|
var lnk = tms.findLinkById(d.get('key'));
|
||||||
|
if (lnk) {
|
||||||
|
return t2d3.transformLabel(lnk.get('position'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function init(_svg_, forceG, _uplink_, _dim_, opts) {
|
||||||
|
|
||||||
|
$log.debug("Initialising Topology Layout");
|
||||||
|
|
||||||
|
settings = angular.extend({}, defaultSettings, opts);
|
||||||
|
|
||||||
|
linkG = forceG.append('g').attr('id', 'topo-links');
|
||||||
|
linkLabelG = forceG.append('g').attr('id', 'topo-linkLabels');
|
||||||
|
numLinkLabelsG = 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');
|
||||||
|
|
||||||
|
force = d3.layout.force()
|
||||||
|
.size(t2vs.getDimensions())
|
||||||
|
.nodes(t2rs.regionNodes())
|
||||||
|
.links(t2rs.regionLinks())
|
||||||
|
.gravity(settings.gravity)
|
||||||
|
.friction(settings.friction)
|
||||||
|
.charge(settings.charge._def_)
|
||||||
|
.linkDistance(settings.linkDistance._def_)
|
||||||
|
.linkStrength(settings.linkStrength._def_)
|
||||||
|
.on('tick', tick);
|
||||||
|
}
|
||||||
|
|
||||||
|
function tick() {
|
||||||
|
// guard against null (which can happen when our view pages out)...
|
||||||
|
if (node && node.size()) {
|
||||||
|
node.attr(tickStuff.nodeAttr);
|
||||||
|
}
|
||||||
|
if (link && link.size()) {
|
||||||
|
link.call(calcPosition)
|
||||||
|
.attr(tickStuff.linkAttr);
|
||||||
|
// t2d3.applyNumLinkLabels(linkNums, numLinkLabelsG);
|
||||||
|
}
|
||||||
|
if (linkLabel && linkLabel.size()) {
|
||||||
|
linkLabel.attr(tickStuff.linkLabelAttr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
_updateNodes();
|
||||||
|
_updateLinks();
|
||||||
|
}
|
||||||
|
|
||||||
|
function _updateNodes() {
|
||||||
|
|
||||||
|
var regionNodes = t2rs.regionNodes();
|
||||||
|
|
||||||
|
// select all the nodes in the layout:
|
||||||
|
node = nodeG.selectAll('.node')
|
||||||
|
.data(regionNodes, function (d) { return d.get('id'); });
|
||||||
|
|
||||||
|
var entering = node.enter()
|
||||||
|
.append('g')
|
||||||
|
.attr({
|
||||||
|
id: function (d) { return sus.safeId(d.get('id')); },
|
||||||
|
class: function (d) { return d.svgClassName() },
|
||||||
|
transform: function (d) {
|
||||||
|
// Need to guard against NaN here ??
|
||||||
|
return sus.translate(d.node.x, d.node.y);
|
||||||
|
},
|
||||||
|
opacity: 0
|
||||||
|
})
|
||||||
|
// .on('mouseover', tss.nodeMouseOver)
|
||||||
|
// .on('mouseout', tss.nodeMouseOut)
|
||||||
|
.transition()
|
||||||
|
.attr('opacity', 1);
|
||||||
|
|
||||||
|
entering.filter('.device').each(t2d3.deviceEnter);
|
||||||
|
entering.filter('.host').each(t2d3.hostEnter);
|
||||||
|
|
||||||
|
// operate on both existing and new nodes:
|
||||||
|
// node.filter('.device').each(function (device) {
|
||||||
|
// t2d3.updateDeviceColors(device);
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
|
function _updateLinks() {
|
||||||
|
|
||||||
|
// var th = ts.theme();
|
||||||
|
var regionLinks = t2rs.regionLinks();
|
||||||
|
|
||||||
|
link = linkG.selectAll('.link')
|
||||||
|
.data(regionLinks, function (d) { return d.get('key'); });
|
||||||
|
|
||||||
|
// operate on existing links:
|
||||||
|
link.each(function (d) {
|
||||||
|
// this is supposed to be an existing link, but we have observed
|
||||||
|
// occasions (where links are deleted and added rapidly?) where
|
||||||
|
// the DOM element has not been defined. So protect against that...
|
||||||
|
if (d.el) {
|
||||||
|
restyleLinkElement(d, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// operate on entering links:
|
||||||
|
var entering = link.enter()
|
||||||
|
.append('line')
|
||||||
|
.call(calcPosition)
|
||||||
|
.attr({
|
||||||
|
x1: function (d) { return d.get('position').x1; },
|
||||||
|
y1: function (d) { return d.get('position').y1; },
|
||||||
|
x2: function (d) { return d.get('position').x2; },
|
||||||
|
y2: function (d) { return d.get('position').y2; },
|
||||||
|
stroke: linkConfig['light'].inColor,
|
||||||
|
'stroke-width': linkConfig.inWidth
|
||||||
|
});
|
||||||
|
|
||||||
|
entering.each(t2d3.linkEntering);
|
||||||
|
|
||||||
|
// operate on both existing and new links:
|
||||||
|
//link.each(...)
|
||||||
|
|
||||||
|
// add labels for how many links are in a thick line
|
||||||
|
// t2d3.applyNumLinkLabels(linkNums, numLinkLabelsG);
|
||||||
|
|
||||||
|
// apply or remove labels
|
||||||
|
// t2d3.applyLinkLabels();
|
||||||
|
|
||||||
|
// operate on exiting links:
|
||||||
|
link.exit()
|
||||||
|
.attr('stroke-dasharray', '3 3')
|
||||||
|
.attr('stroke', linkConfig['light'].outColor)
|
||||||
|
.style('opacity', 0.5)
|
||||||
|
.transition()
|
||||||
|
.duration(1500)
|
||||||
|
.attr({
|
||||||
|
'stroke-dasharray': '3 12',
|
||||||
|
'stroke-width': linkConfig.outWidth
|
||||||
|
})
|
||||||
|
.style('opacity', 0.0)
|
||||||
|
.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
function calcPosition() {
|
||||||
|
var lines = this,
|
||||||
|
linkSrcId,
|
||||||
|
linkNums = [];
|
||||||
|
|
||||||
|
lines.each(function (d) {
|
||||||
|
if (d.get('type') === 'hostLink') {
|
||||||
|
d.set('position', getDefaultPos(d));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function normalizeLinkSrc(link) {
|
||||||
|
// ensure source device is consistent across set of links
|
||||||
|
// temporary measure until link modeling is refactored
|
||||||
|
if (!linkSrcId) {
|
||||||
|
linkSrcId = link.source.id;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return link.source.id !== linkSrcId;
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.each(function (d) {
|
||||||
|
d.set('position', getDefaultPos(d));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDefaultPos(link) {
|
||||||
|
|
||||||
|
return {
|
||||||
|
x1: link.get('source').x,
|
||||||
|
y1: link.get('source').y,
|
||||||
|
x2: link.get('target').x,
|
||||||
|
y2: link.get('target').y
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDimensions() {
|
||||||
|
if (force) {
|
||||||
|
force.size(t2vs.getDimensions());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function start() {
|
||||||
|
force.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
angular.module('ovTopo2')
|
||||||
|
.factory('Topo2LayoutService',
|
||||||
|
[
|
||||||
|
'$log', 'SvgUtilService', 'Topo2RegionService',
|
||||||
|
'Topo2D3Service', 'Topo2ViewService',
|
||||||
|
|
||||||
|
function (_$log_, _sus_, _t2rs_, _t2d3_, _t2vs_) {
|
||||||
|
|
||||||
|
$log = _$log_;
|
||||||
|
t2rs = _t2rs_;
|
||||||
|
t2d3 = _t2d3_;
|
||||||
|
t2vs = _t2vs_;
|
||||||
|
sus = _sus_;
|
||||||
|
|
||||||
|
return {
|
||||||
|
init: init,
|
||||||
|
update: update,
|
||||||
|
start: start,
|
||||||
|
|
||||||
|
setDimensions: setDimensions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
);
|
||||||
|
})();
|
@ -22,12 +22,162 @@
|
|||||||
(function () {
|
(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var Collection, Model;
|
var Collection, Model, region, ts;
|
||||||
|
|
||||||
function createLinkCollection(data, region) {
|
var widthRatio = 1.4,
|
||||||
|
linkScale = d3.scale.linear()
|
||||||
|
.domain([1, 12])
|
||||||
|
.range([widthRatio, 12 * widthRatio])
|
||||||
|
.clamp(true),
|
||||||
|
allLinkTypes = 'direct indirect optical tunnel UiDeviceLink',
|
||||||
|
allLinkSubTypes = 'inactive not-permitted';
|
||||||
|
|
||||||
|
// configuration
|
||||||
|
var linkConfig = {
|
||||||
|
light: {
|
||||||
|
baseColor: '#939598',
|
||||||
|
inColor: '#66f',
|
||||||
|
outColor: '#f00'
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
|
// TODO : theme
|
||||||
|
baseColor: '#939598',
|
||||||
|
inColor: '#66f',
|
||||||
|
outColor: '#f00'
|
||||||
|
},
|
||||||
|
inWidth: 12,
|
||||||
|
outWidth: 10
|
||||||
|
};
|
||||||
|
|
||||||
|
var defaultLinkType = 'direct',
|
||||||
|
nearDist = 15;
|
||||||
|
|
||||||
|
function createLink() {
|
||||||
|
|
||||||
|
var linkPoints = this.linkEndPoints(this.get('epA'), this.get('epB'));
|
||||||
|
console.log(this);
|
||||||
|
|
||||||
|
var attrs = angular.extend({}, linkPoints, {
|
||||||
|
key: this.get('id'),
|
||||||
|
class: 'link',
|
||||||
|
weight: 1,
|
||||||
|
srcPort: this.get('srcPort'),
|
||||||
|
tgtPort: this.get('dstPort'),
|
||||||
|
position: {
|
||||||
|
x1: 0,
|
||||||
|
y1: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 0
|
||||||
|
}
|
||||||
|
// functions to aggregate dual link state
|
||||||
|
// extra: link.extra
|
||||||
|
});
|
||||||
|
|
||||||
|
this.set(attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
function linkEndPoints(srcId, dstId) {
|
||||||
|
|
||||||
|
var sourceNode = this.region.get('devices').get(srcId.substring(0, srcId.length -2));
|
||||||
|
var targetNode = this.region.get('devices').get(dstId.substring(0, dstId.length -2));
|
||||||
|
|
||||||
|
// var srcNode = lu[srcId],
|
||||||
|
// dstNode = lu[dstId],
|
||||||
|
// sMiss = !srcNode ? missMsg('src', srcId) : '',
|
||||||
|
// dMiss = !dstNode ? missMsg('dst', dstId) : '';
|
||||||
|
//
|
||||||
|
// if (sMiss || dMiss) {
|
||||||
|
// $log.error('Node(s) not on map for link:' + sMiss + dMiss);
|
||||||
|
// //logicError('Node(s) not on map for link:\n' + sMiss + dMiss);
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
|
||||||
|
this.source = sourceNode.toJSON();
|
||||||
|
this.target = targetNode.toJSON();
|
||||||
|
|
||||||
|
return {
|
||||||
|
source: sourceNode,
|
||||||
|
target: targetNode
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createLinkCollection(data, _region) {
|
||||||
|
|
||||||
|
var LinkModel = Model.extend({
|
||||||
|
region: _region,
|
||||||
|
createLink: createLink,
|
||||||
|
linkEndPoints: linkEndPoints,
|
||||||
|
type: function () {
|
||||||
|
return this.get('type');
|
||||||
|
},
|
||||||
|
expected: function () {
|
||||||
|
//TODO: original code is: (s && s.expected) && (t && t.expected);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
online: function () {
|
||||||
|
return true;
|
||||||
|
return both && (s && s.online) && (t && t.online);
|
||||||
|
},
|
||||||
|
linkWidth: function () {
|
||||||
|
var s = this.get('fromSource'),
|
||||||
|
t = this.get('fromTarget'),
|
||||||
|
ws = (s && s.linkWidth) || 0,
|
||||||
|
wt = (t && t.linkWidth) || 0;
|
||||||
|
|
||||||
|
// console.log(s);
|
||||||
|
// TODO: Current json is missing linkWidth
|
||||||
|
return 1.2;
|
||||||
|
return this.get('position').multiLink ? 5 : Math.max(ws, wt);
|
||||||
|
},
|
||||||
|
|
||||||
|
restyleLinkElement: function (immediate) {
|
||||||
|
// this fn's job is to look at raw links and decide what svg classes
|
||||||
|
// need to be applied to the line element in the DOM
|
||||||
|
var th = ts.theme(),
|
||||||
|
el = this.el,
|
||||||
|
type = this.get('type'),
|
||||||
|
lw = this.linkWidth(),
|
||||||
|
online = this.online(),
|
||||||
|
modeCls = this.expected() ? 'inactive' : 'not-permitted',
|
||||||
|
delay = immediate ? 0 : 1000;
|
||||||
|
|
||||||
|
console.log(type);
|
||||||
|
|
||||||
|
// NOTE: understand why el is sometimes undefined on addLink events...
|
||||||
|
// Investigated:
|
||||||
|
// el is undefined when it's a reverse link that is being added.
|
||||||
|
// updateLinks (which sets ldata.el) isn't called before this is called.
|
||||||
|
// Calling _updateLinks in addLinkUpdate fixes it, but there might be
|
||||||
|
// a more efficient way to fix it.
|
||||||
|
if (el && !el.empty()) {
|
||||||
|
el.classed('link', true);
|
||||||
|
el.classed(allLinkSubTypes, false);
|
||||||
|
el.classed(modeCls, !online);
|
||||||
|
el.classed(allLinkTypes, false);
|
||||||
|
if (type) {
|
||||||
|
el.classed(type, true);
|
||||||
|
}
|
||||||
|
el.transition()
|
||||||
|
.duration(delay)
|
||||||
|
.attr('stroke-width', linkScale(lw))
|
||||||
|
.attr('stroke', linkConfig[th].baseColor);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onEnter: function (el) {
|
||||||
|
var link = d3.select(el);
|
||||||
|
this.el = link;
|
||||||
|
|
||||||
|
this.restyleLinkElement();
|
||||||
|
|
||||||
|
if (this.get('type') === 'hostLink') {
|
||||||
|
sus.visible(link, api.showHosts());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
var LinkCollection = Collection.extend({
|
var LinkCollection = Collection.extend({
|
||||||
model: Model
|
model: LinkModel,
|
||||||
});
|
});
|
||||||
|
|
||||||
return new LinkCollection(data);
|
return new LinkCollection(data);
|
||||||
@ -35,12 +185,13 @@
|
|||||||
|
|
||||||
angular.module('ovTopo2')
|
angular.module('ovTopo2')
|
||||||
.factory('Topo2LinkService',
|
.factory('Topo2LinkService',
|
||||||
['Topo2Collection', 'Topo2Model',
|
['Topo2Collection', 'Topo2Model', 'ThemeService',
|
||||||
|
|
||||||
function (_Collection_, _Model_) {
|
function (_Collection_, _Model_, _ts_) {
|
||||||
|
|
||||||
|
ts = _ts_;
|
||||||
Collection = _Collection_;
|
Collection = _Collection_;
|
||||||
Model = _Model_.extend({});
|
Model = _Model_;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
createLinkCollection: createLinkCollection
|
createLinkCollection: createLinkCollection
|
||||||
|
@ -28,17 +28,86 @@
|
|||||||
this.attributes = {};
|
this.attributes = {};
|
||||||
|
|
||||||
attrs = angular.extend({}, attrs);
|
attrs = angular.extend({}, attrs);
|
||||||
this.set(attrs);
|
this.set(attrs, { silent: true });
|
||||||
|
this.initialize.apply(this, arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
Model.prototype = {
|
Model.prototype = {
|
||||||
|
|
||||||
|
initialize: function () {},
|
||||||
|
|
||||||
|
onChange: function (property, value, options) {},
|
||||||
|
|
||||||
get: function (attr) {
|
get: function (attr) {
|
||||||
return this.attributes[attr];
|
return this.attributes[attr];
|
||||||
},
|
},
|
||||||
|
|
||||||
set: function(data) {
|
set: function(key, val, options) {
|
||||||
angular.extend(this.attributes, data);
|
|
||||||
|
if (!key) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
var attributes;
|
||||||
|
if (typeof key === 'object') {
|
||||||
|
attributes = key;
|
||||||
|
options = val;
|
||||||
|
} else {
|
||||||
|
(attributes = {})[key] = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
options || (options = {});
|
||||||
|
|
||||||
|
var unset = options.unset,
|
||||||
|
silent = options.silent,
|
||||||
|
changes = [],
|
||||||
|
changing = this._changing;
|
||||||
|
|
||||||
|
this._changing = true;
|
||||||
|
|
||||||
|
if (!changing) {
|
||||||
|
|
||||||
|
// NOTE: angular.copy causes issues in chrome
|
||||||
|
this._previousAttributes = Object.create(Object.getPrototypeOf(this.attributes));
|
||||||
|
this.changed = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
var current = this.attributes,
|
||||||
|
changed = this.changed,
|
||||||
|
previous = this._previousAttributes;
|
||||||
|
|
||||||
|
angular.forEach(attributes, function (attribute, index) {
|
||||||
|
|
||||||
|
val = attribute;
|
||||||
|
|
||||||
|
if (!angular.equals(current[index], val)) {
|
||||||
|
changes.push(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!angular.equals(previous[index], val)) {
|
||||||
|
changed[index] = val;
|
||||||
|
} else {
|
||||||
|
delete changed[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
unset ? delete current[index] : current[index] = val;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Trigger all relevant attribute changes.
|
||||||
|
if (!silent) {
|
||||||
|
if (changes.length) {
|
||||||
|
this._pending = options;
|
||||||
|
}
|
||||||
|
for (var i = 0; i < changes.length; i++) {
|
||||||
|
this.onChange(changes[i], this, current[changes[i]], options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._changing = false;
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
toJSON: function(options) {
|
||||||
|
return angular.copy(this.attributes)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
134
web/gui/src/main/webapp/app/view/topo2/topo2NodeModel.js
Normal file
134
web/gui/src/main/webapp/app/view/topo2/topo2NodeModel.js
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* 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 Layout Module.
|
||||||
|
Module that contains the d3.force.layout logic
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var randomService;
|
||||||
|
var fn;
|
||||||
|
|
||||||
|
//internal state;
|
||||||
|
var defaultLinkType = 'direct',
|
||||||
|
nearDist = 15;
|
||||||
|
|
||||||
|
function positionNode(node, forUpdate) {
|
||||||
|
|
||||||
|
var meta = node.metaUi,
|
||||||
|
x = meta && meta.x,
|
||||||
|
y = meta && meta.y,
|
||||||
|
dim = [800, 600],
|
||||||
|
xy;
|
||||||
|
|
||||||
|
// if the device contains explicit LONG/LAT data, use that to position
|
||||||
|
if (setLongLat(node)) {
|
||||||
|
//indicate we want to update cached meta data...
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// else if we have [x,y] cached in meta data, use that...
|
||||||
|
if (x !== undefined && y !== undefined) {
|
||||||
|
node.fixed = true;
|
||||||
|
node.px = node.x = x;
|
||||||
|
node.py = node.y = y;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if this is a node update (not a node add).. skip randomizer
|
||||||
|
if (forUpdate) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: Placing incoming unpinned nodes at exactly the same point
|
||||||
|
// (center of the view) causes them to explode outwards when
|
||||||
|
// the force layout kicks in. So, we spread them out a bit
|
||||||
|
// initially, to provide a more serene layout convergence.
|
||||||
|
// Additionally, if the node is a host, we place it near
|
||||||
|
// the device it is connected to.
|
||||||
|
|
||||||
|
function rand() {
|
||||||
|
return {
|
||||||
|
x: randomService.randDim(dim[0]),
|
||||||
|
y: randomService.randDim(dim[1])
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function near(node) {
|
||||||
|
return {
|
||||||
|
x: node.x + nearDist + randomService.spread(nearDist),
|
||||||
|
y: node.y + nearDist + randomService.spread(nearDist)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDevice(cp) {
|
||||||
|
// console.log(cp);
|
||||||
|
// var d = lu[cp.device];
|
||||||
|
// return d || rand();
|
||||||
|
return rand();
|
||||||
|
}
|
||||||
|
|
||||||
|
xy = (node.class === 'host') ? near(getDevice(node.cp)) : rand();
|
||||||
|
angular.extend(node, xy);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLongLat(node) {
|
||||||
|
var loc = node.location,
|
||||||
|
coord;
|
||||||
|
|
||||||
|
if (loc && loc.type === 'lnglat') {
|
||||||
|
coord = [0, 0];
|
||||||
|
node.fixed = true;
|
||||||
|
node.px = node.x = coord[0];
|
||||||
|
node.py = node.y = coord[1];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
angular.module('ovTopo2')
|
||||||
|
.factory('Topo2NodeModel',
|
||||||
|
['Topo2Model', 'FnService', 'RandomService',
|
||||||
|
function (Model, _fn_, _RandomService_) {
|
||||||
|
|
||||||
|
randomService = _RandomService_;
|
||||||
|
fn = _fn_;
|
||||||
|
|
||||||
|
return Model.extend({
|
||||||
|
initialize: function () {
|
||||||
|
this.node = this.createNode();
|
||||||
|
},
|
||||||
|
svgClassName: function () {
|
||||||
|
return fn.classNames('node', this.nodeType, this.get('type'), {
|
||||||
|
online: this.get('online')
|
||||||
|
});
|
||||||
|
},
|
||||||
|
createNode: function () {
|
||||||
|
|
||||||
|
var node = angular.extend({}, this.attributes);
|
||||||
|
|
||||||
|
// Augment as needed...
|
||||||
|
node.class = this.nodeType;
|
||||||
|
node.svgClass = this.svgClassName();
|
||||||
|
positionNode(node);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}]
|
||||||
|
);
|
||||||
|
})();
|
@ -24,12 +24,13 @@
|
|||||||
|
|
||||||
var $log,
|
var $log,
|
||||||
wss,
|
wss,
|
||||||
|
Model,
|
||||||
t2sr,
|
t2sr,
|
||||||
t2ds,
|
t2ds,
|
||||||
t2hs,
|
t2hs,
|
||||||
t2ls;
|
t2ls;
|
||||||
|
|
||||||
var regions;
|
var region;
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
regions = {};
|
regions = {};
|
||||||
@ -37,25 +38,46 @@
|
|||||||
|
|
||||||
function addRegion(data) {
|
function addRegion(data) {
|
||||||
|
|
||||||
var region = {
|
region = new Model({
|
||||||
subregions: t2sr.createSubRegionCollection(data.subregions),
|
id: data.id,
|
||||||
devices: t2ds.createDeviceCollection(data.devices, data),
|
layerOrder: data.layerOrder
|
||||||
hosts: t2hs.createHostCollection(data.hosts),
|
});
|
||||||
links: t2ls.createLinkCollection(data.links),
|
|
||||||
};
|
region.set({
|
||||||
|
subregions: t2sr.createSubRegionCollection(data.subregions, region),
|
||||||
|
devices: t2ds.createDeviceCollection(data.devices, region),
|
||||||
|
hosts: t2hs.createHostCollection(data.hosts, region),
|
||||||
|
links: t2ls.createLinkCollection(data.links, region),
|
||||||
|
});
|
||||||
|
|
||||||
|
region.set('test', 2);
|
||||||
|
|
||||||
|
angular.forEach(region.get('links').models, function (link) {
|
||||||
|
link.createLink();
|
||||||
|
});
|
||||||
|
|
||||||
$log.debug('Region: ', region);
|
$log.debug('Region: ', region);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function regionNodes() {
|
||||||
|
return [].concat(region.get('devices').models, region.get('hosts').models);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function regionLinks() {
|
||||||
|
return region.get('links').models;
|
||||||
|
}
|
||||||
|
|
||||||
angular.module('ovTopo2')
|
angular.module('ovTopo2')
|
||||||
.factory('Topo2RegionService',
|
.factory('Topo2RegionService',
|
||||||
['$log', 'WebSocketService', 'Topo2SubRegionService', 'Topo2DeviceService',
|
['$log', 'WebSocketService', 'Topo2Model', 'Topo2SubRegionService', 'Topo2DeviceService',
|
||||||
'Topo2HostService', 'Topo2LinkService',
|
'Topo2HostService', 'Topo2LinkService',
|
||||||
|
|
||||||
function (_$log_, _wss_, _t2sr_, _t2ds_, _t2hs_, _t2ls_) {
|
function (_$log_, _wss_, _Model_, _t2sr_, _t2ds_, _t2hs_, _t2ls_) {
|
||||||
|
|
||||||
$log = _$log_;
|
$log = _$log_;
|
||||||
wss = _wss_;
|
wss = _wss_;
|
||||||
|
Model = _Model_
|
||||||
t2sr = _t2sr_;
|
t2sr = _t2sr_;
|
||||||
t2ds = _t2ds_;
|
t2ds = _t2ds_;
|
||||||
t2hs = _t2hs_;
|
t2hs = _t2hs_;
|
||||||
@ -65,6 +87,9 @@
|
|||||||
init: init,
|
init: init,
|
||||||
|
|
||||||
addRegion: addRegion,
|
addRegion: addRegion,
|
||||||
|
regionNodes: regionNodes,
|
||||||
|
regionLinks: regionLinks,
|
||||||
|
|
||||||
getSubRegions: t2sr.getSubRegions
|
getSubRegions: t2sr.getSubRegions
|
||||||
};
|
};
|
||||||
}]);
|
}]);
|
||||||
|
46
web/gui/src/main/webapp/app/view/topo2/topo2View.js
Normal file
46
web/gui/src/main/webapp/app/view/topo2/topo2View.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* 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 Layout Module.
|
||||||
|
Module that contains the d3.force.layout logic
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var dimensions;
|
||||||
|
|
||||||
|
function newDim(_dimensions) {
|
||||||
|
dimensions = _dimensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDimensions() {
|
||||||
|
return dimensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
angular.module('ovTopo2')
|
||||||
|
.factory('Topo2ViewService',
|
||||||
|
[
|
||||||
|
function () {
|
||||||
|
return {
|
||||||
|
newDim: newDim,
|
||||||
|
getDimensions: getDimensions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
);
|
||||||
|
})();
|
@ -128,15 +128,21 @@
|
|||||||
<!-- Under development for Region support. -->
|
<!-- Under development for Region support. -->
|
||||||
<script src="app/view/topo2/topo2.js"></script>
|
<script src="app/view/topo2/topo2.js"></script>
|
||||||
<script src="app/view/topo2/topo2Collection.js"></script>
|
<script src="app/view/topo2/topo2Collection.js"></script>
|
||||||
|
<script src="app/view/topo2/topo2D3.js"></script>
|
||||||
<script src="app/view/topo2/topo2Device.js"></script>
|
<script src="app/view/topo2/topo2Device.js"></script>
|
||||||
<script src="app/view/topo2/topo2Model.js"></script>
|
|
||||||
<script src="app/view/topo2/topo2Event.js"></script>
|
<script src="app/view/topo2/topo2Event.js"></script>
|
||||||
<script src="app/view/topo2/topo2Force.js"></script>
|
<script src="app/view/topo2/topo2Force.js"></script>
|
||||||
<script src="app/view/topo2/topo2Host.js"></script>
|
<script src="app/view/topo2/topo2Host.js"></script>
|
||||||
<script src="app/view/topo2/topo2Instance.js"></script>
|
<script src="app/view/topo2/topo2Instance.js"></script>
|
||||||
|
<script src="app/view/topo2/topo2Layout.js"></script>
|
||||||
<script src="app/view/topo2/topo2Link.js"></script>
|
<script src="app/view/topo2/topo2Link.js"></script>
|
||||||
|
<script src="app/view/topo2/topo2Model.js"></script>
|
||||||
|
<script src="app/view/topo2/topo2NodeModel.js"></script>
|
||||||
<script src="app/view/topo2/topo2Region.js"></script>
|
<script src="app/view/topo2/topo2Region.js"></script>
|
||||||
|
<script src="app/view/topo2/topo2Select.js"></script>
|
||||||
<script src="app/view/topo2/topo2SubRegion.js"></script>
|
<script src="app/view/topo2/topo2SubRegion.js"></script>
|
||||||
|
<script src="app/view/topo2/topo2Theme.js"></script>
|
||||||
|
<script src="app/view/topo2/topo2View.js"></script>
|
||||||
<link rel="stylesheet" href="app/view/topo2/topo2.css">
|
<link rel="stylesheet" href="app/view/topo2/topo2.css">
|
||||||
<link rel="stylesheet" href="app/view/topo2/topo2-theme.css">
|
<link rel="stylesheet" href="app/view/topo2/topo2-theme.css">
|
||||||
|
|
||||||
|
@ -216,7 +216,8 @@ describe('factory: fw/util/fn.js', function() {
|
|||||||
'isMobile', 'isChrome', 'isSafari', 'isFirefox',
|
'isMobile', 'isChrome', 'isSafari', 'isFirefox',
|
||||||
'debugOn', 'debug',
|
'debugOn', 'debug',
|
||||||
'find', 'inArray', 'removeFromArray', 'isEmptyObject', 'sameObjProps', 'containsObj', 'cap',
|
'find', 'inArray', 'removeFromArray', 'isEmptyObject', 'sameObjProps', 'containsObj', 'cap',
|
||||||
'eecode', 'noPx', 'noPxStyle', 'endsWith', 'parseBitRate', 'addToTrie', 'removeFromTrie', 'trieLookup'
|
'eecode', 'noPx', 'noPxStyle', 'endsWith', 'parseBitRate', 'addToTrie', 'removeFromTrie', 'trieLookup',
|
||||||
|
'classNames'
|
||||||
])).toBeTruthy();
|
])).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
15
web/gui/src/test/_karma/package.json
Normal file
15
web/gui/src/test/_karma/package.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"name": "karma",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "mockserver.js",
|
||||||
|
"dependencies": {
|
||||||
|
"websocket": "^1.0.23"
|
||||||
|
},
|
||||||
|
"devDependencies": {},
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC"
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user