GUI -- Implemented Show/Hide Offline devices & Show/Hide Hosts (also used Flash Service).
- added 'toggle(cb)' to panel API.
- deferred keybindings to allow direct reference to sub-API functions.
- re-implemented tick() function.
- added 'list scenarios' command to mockserver.
Change-Id: I1cc0009266e1015747b1d8106bd1f088adb2feb5
diff --git a/web/gui/src/main/webapp/app/fw/layer/panel.js b/web/gui/src/main/webapp/app/fw/layer/panel.js
index 4df0d72..a29c175 100644
--- a/web/gui/src/main/webapp/app/fw/layer/panel.js
+++ b/web/gui/src/main/webapp/app/fw/layer/panel.js
@@ -71,6 +71,7 @@
api = {
show: showPanel,
hide: hidePanel,
+ toggle: togglePanel,
empty: emptyPanel,
append: appendPanel,
width: panelWidth,
@@ -111,6 +112,14 @@
.style('opacity', 0);
}
+ function togglePanel(cb) {
+ if (p.on) {
+ hidePanel(cb);
+ } else {
+ showPanel(cb);
+ }
+ }
+
function emptyPanel() {
return p.el.html('');
}
diff --git a/web/gui/src/main/webapp/app/fw/svg/svgUtil.js b/web/gui/src/main/webapp/app/fw/svg/svgUtil.js
index 3a35e9f..e99da3c 100644
--- a/web/gui/src/main/webapp/app/fw/svg/svgUtil.js
+++ b/web/gui/src/main/webapp/app/fw/svg/svgUtil.js
@@ -141,7 +141,7 @@
lightMute = ['#A8B8CC', '#CCB3A8', '#FFC2BD', '#96D6BF', '#D19FCE', '#8FCCCA', '#CAEAA4'],
darkNorm = ['#304860', '#664631', '#A8391B', '#00754B', '#77206D', '#005959', '#428700'],
- darkMute = ['#16203A', '#281810', '#4F1206', '#00331C', '#3D063A', '#002D2D', '#1B4400'];
+ darkMute = ['#304860', '#664631', '#A8391B', '#00754B', '#77206D', '#005959', '#428700'];
var colors= {
light: {
diff --git a/web/gui/src/main/webapp/app/view/topo/topo.js b/web/gui/src/main/webapp/app/view/topo/topo.js
index 4a2ae8a..f7568f5 100644
--- a/web/gui/src/main/webapp/app/view/topo/topo.js
+++ b/web/gui/src/main/webapp/app/view/topo/topo.js
@@ -36,75 +36,61 @@
// Internal state
var zoomer;
- // Note: "exported" state should be properties on 'self' variable
-
// --- Short Cut Keys ------------------------------------------------
- var keyBindings = {
- //O: [toggleSummary, 'Toggle ONOS summary pane'],
- I: [toggleInstances, 'Toggle ONOS instances pane'],
- //D: [toggleDetails, 'Disable / enable details pane'],
+ function setUpKeys() {
+ // key bindings need to be made after the services have been injected
+ // thus, deferred to here...
+ ks.keyBindings({
+ //O: [toggleSummary, 'Toggle ONOS summary pane'],
+ I: [toggleInstances, 'Toggle ONOS instances pane'],
+ //D: [toggleDetails, 'Disable / enable details pane'],
- //H: [toggleHosts, 'Toggle host visibility'],
- //M: [toggleOffline, 'Toggle offline visibility'],
- //B: [toggleBg, 'Toggle background image'],
- //P: togglePorts,
+ H: [tfs.toggleHosts, 'Toggle host visibility'],
+ M: [tfs.toggleOffline, 'Toggle offline visibility'],
+ //B: [toggleBg, 'Toggle background image'],
+ //P: togglePorts,
- //X: [toggleNodeLock, 'Lock / unlock node positions'],
- //Z: [toggleOblique, 'Toggle oblique view (Experimental)'],
- L: [cycleLabels, 'Cycle device labels'],
- //U: [unpin, 'Unpin node (hover mouse over)'],
- R: [resetZoom, 'Reset pan / zoom'],
+ //X: [toggleNodeLock, 'Lock / unlock node positions'],
+ //Z: [toggleOblique, 'Toggle oblique view (Experimental)'],
+ L: [tfs.cycleDeviceLabels, 'Cycle device labels'],
+ //U: [unpin, 'Unpin node (hover mouse over)'],
+ R: [resetZoom, 'Reset pan / zoom'],
- //V: [showRelatedIntentsAction, 'Show all related intents'],
- //rightArrow: [showNextIntentAction, 'Show next related intent'],
- //leftArrow: [showPrevIntentAction, 'Show previous related intent'],
- //W: [showSelectedIntentTrafficAction, 'Monitor traffic of selected intent'],
- //A: [showAllTrafficAction, 'Monitor all traffic'],
- //F: [showDeviceLinkFlowsAction, 'Show device link flows'],
+ //V: [showRelatedIntentsAction, 'Show all related intents'],
+ //rightArrow: [showNextIntentAction, 'Show next related intent'],
+ //leftArrow: [showPrevIntentAction, 'Show previous related intent'],
+ //W: [showSelectedIntentTrafficAction, 'Monitor traffic of selected intent'],
+ //A: [showAllTrafficAction, 'Monitor all traffic'],
+ //F: [showDeviceLinkFlowsAction, 'Show device link flows'],
- //E: [equalizeMasters, 'Equalize mastership roles'],
+ //E: [equalizeMasters, 'Equalize mastership roles'],
- //esc: handleEscape,
+ //esc: handleEscape,
- _helpFormat: [
- ['O', 'I', 'D', '-', 'H', 'M', 'B', 'P' ],
- ['X', 'Z', 'L', 'U', 'R' ],
- ['V', 'rightArrow', 'leftArrow', 'W', 'A', 'F', '-', 'E' ]
- ]
+ _helpFormat: [
+ ['O', 'I', 'D', '-', 'H', 'M', 'B', 'P' ],
+ ['X', 'Z', 'L', 'U', 'R' ],
+ ['V', 'rightArrow', 'leftArrow', 'W', 'A', 'F', '-', 'E' ]
+ ]
+ });
- };
+ // TODO: // mouse gestures
+ var gestures = [
+ ['click', 'Select the item and show details'],
+ ['shift-click', 'Toggle selection state'],
+ ['drag', 'Reposition (and pin) device / host'],
+ ['cmd-scroll', 'Zoom in / out'],
+ ['cmd-drag', 'Pan']
+ ];
+ }
- // mouse gestures
- var gestures = [
- ['click', 'Select the item and show details'],
- ['shift-click', 'Toggle selection state'],
- ['drag', 'Reposition (and pin) device / host'],
- ['cmd-scroll', 'Zoom in / out'],
- ['cmd-drag', 'Pan']
- ];
function toggleInstances() {
- if (tis.isVisible()) {
- tis.hide();
- } else {
- tis.show();
- }
+ tis.toggle();
tfs.updateDeviceColors();
}
- function cycleLabels() {
- $log.debug('Cycle Labels.....');
- }
-
- function resetZoom() {
- zoomer.reset();
- }
-
- function setUpKeys() {
- ks.keyBindings(keyBindings);
- }
-
// --- Glyphs, Icons, and the like -----------------------------------
@@ -122,8 +108,7 @@
}
function zoomCallback() {
- var tr = zoomer.translate(),
- sc = zoomer.scale();
+ var sc = zoomer.scale();
// keep the map lines constant width while zooming
mapG.style('stroke-width', (2.0 / sc) + 'px');
@@ -139,6 +124,10 @@
});
}
+ function resetZoom() {
+ zoomer.reset();
+ }
+
// callback invoked when the SVG view has been resized..
function svgResized(dim) {
diff --git a/web/gui/src/main/webapp/app/view/topo/topoForce.js b/web/gui/src/main/webapp/app/view/topo/topoForce.js
index 01b719e..b3c8eaa 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoForce.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoForce.js
@@ -23,9 +23,7 @@
'use strict';
// injected refs
- var $log, fs, sus, is, ts, tis, uplink;
-
- var icfg;
+ var $log, fs, sus, is, ts, flash, tis, icfg, uplink;
// configuration
var labelConfig = {
@@ -53,9 +51,9 @@
outColor: '#f00',
},
dark: {
- baseColor: '#666',
+ baseColor: '#aaa',
inColor: '#66f',
- outColor: '#f00',
+ outColor: '#f66'
},
inWidth: 12,
outWidth: 10
@@ -74,7 +72,9 @@
lu = network.lookup, // shorthand
deviceLabelIndex = 0, // for device label cycling
hostLabelIndex = 0, // for host label cycling
- showHosts = 1, // whether hosts are displayed
+ showHosts = true, // whether hosts are displayed
+ showOffline = true, // whether offline devices are displayed
+ oblique = false, // whether we are in the oblique view
width, height;
// SVG elements;
@@ -150,9 +150,8 @@
}
updateNodes();
if (wasOnline !== d.online) {
- // TODO: re-instate link update, and offline visibility
- //findAttachedLinks(d.id).forEach(restyleLinkElement);
- //updateOfflineVisibility(d);
+ findAttachedLinks(d.id).forEach(restyleLinkElement);
+ updateOfflineVisibility(d);
}
} else {
// TODO: decide whether we want to capture logic errors
@@ -338,9 +337,8 @@
linkScale = d3.scale.linear()
.domain([1, 12])
.range([widthRatio, 12 * widthRatio])
- .clamp(true);
-
- var allLinkTypes = 'direct indirect optical tunnel',
+ .clamp(true),
+ allLinkTypes = 'direct indirect optical tunnel',
defaultLinkType = 'direct';
function restyleLinkElement(ldata) {
@@ -364,6 +362,12 @@
.attr('stroke', linkConfig[th].baseColor);
}
+ function findLinkById(id) {
+ // check to see if this is a reverse lookup, else default to given id
+ var key = network.revLinkToKey[id] || id;
+ return key && lu[key];
+ }
+
function findLink(linkData, op) {
var key = makeLinkKey(linkData),
keyrev = makeLinkKey(linkData, 1),
@@ -436,6 +440,15 @@
return result;
}
+ function findOfflineNodes() {
+ var a = [];
+ network.nodes.forEach(function (d) {
+ if (d.class === 'device' && !d.online) {
+ a.push(d);
+ }
+ });
+ return a;
+ }
function findAttachedHosts(devId) {
var hosts = [];
@@ -513,6 +526,36 @@
fResume();
}
+ function updateHostVisibility() {
+ sus.makeVisible(nodeG.selectAll('.host'), showHosts);
+ sus.makeVisible(linkG.selectAll('.hostLink'), showHosts);
+ }
+
+ function updateOfflineVisibility(dev) {
+ function updDev(d, show) {
+ sus.makeVisible(d.el, show);
+
+ findAttachedLinks(d.id).forEach(function (link) {
+ b = show && ((link.type() !== 'hostLink') || showHosts);
+ sus.makeVisible(link.el, b);
+ });
+ findAttachedHosts(d.id).forEach(function (host) {
+ b = show && showHosts;
+ sus.makeVisible(host.el, b);
+ });
+ }
+
+ if (dev) {
+ // updating a specific device that just toggled off/on-line
+ updDev(dev, dev.online || showOffline);
+ } else {
+ // updating all offline devices
+ findOfflineNodes().forEach(function (d) {
+ updDev(d, showOffline);
+ });
+ }
+ }
+
function sendUpdateMeta(d, store) {
var metaUi = {},
@@ -536,16 +579,6 @@
}
- function fStart() {
- $log.debug('TODO fStart()...');
- // TODO...
- }
-
- function fResume() {
- $log.debug('TODO fResume()...');
- // TODO...
- }
-
// ==========================
// === Devices and hosts - helper functions
@@ -817,6 +850,28 @@
}
}
+ function vis(b) {
+ return b ? 'visible' : 'hidden';
+ }
+
+ function toggleHosts() {
+ showHosts = !showHosts;
+ updateHostVisibility();
+ flash.flash('Hosts ' + vis(showHosts));
+ }
+
+ function toggleOffline() {
+ showOffline = !showOffline;
+ updateOfflineVisibility();
+ flash.flash('Offline devices ' + vis(showOffline));
+ }
+
+ function cycleDeviceLabels() {
+ // TODO cycle device labels
+ }
+
+ // ==========================================
+
var dCol = {
black: '#000',
paleblue: '#acf',
@@ -1070,12 +1125,12 @@
// operate on exiting links:
link.exit()
.attr('stroke-dasharray', '3 3')
+ .attr('stroke', linkConfig[th].outColor)
.style('opacity', 0.5)
.transition()
.duration(1500)
.attr({
'stroke-dasharray': '3 12',
- stroke: linkConfig[th].outColor,
'stroke-width': linkConfig.outWidth
})
.style('opacity', 0.0)
@@ -1084,7 +1139,7 @@
// NOTE: invoke a single tick to force the labels to position
// onto their links.
tick();
- // FIXME: this is a bug when in oblique view
+ // TODO: this causes undesirable behavior when in oblique view
// It causes the nodes to jump into "overhead" view positions, even
// though the oblique planes are still showing...
}
@@ -1191,14 +1246,55 @@
// ==========================
// force layout tick function
- function tick() {
+ function fResume() {
+ if (!oblique) {
+ force.resume();
+ }
+ }
+
+ function fStart() {
+ if (!oblique) {
+ force.start();
+ }
+ }
+
+ var tickStuff = {
+ nodeAttr: {
+ transform: function (d) { return sus.translate(d.x, d.y); }
+ },
+ linkAttr: {
+ x1: function (d) { return d.source.x; },
+ y1: function (d) { return d.source.y; },
+ x2: function (d) { return d.target.x; },
+ y2: function (d) { return d.target.y; }
+ },
+ linkLabelAttr: {
+ transform: function (d) {
+ var lnk = findLinkById(d.key);
+ if (lnk) {
+ return transformLabel({
+ x1: lnk.source.x,
+ y1: lnk.source.y,
+ x2: lnk.target.x,
+ y2: lnk.target.y
+ });
+ }
+ }
+ }
+ };
+
+ function tick() {
+ node.attr(tickStuff.nodeAttr);
+ link.attr(tickStuff.linkAttr);
+ linkLabel.attr(tickStuff.linkLabelAttr);
}
// ==========================
// === MOUSE GESTURE HANDLERS
+ // FIXME:
function selectCb() { }
function atDragEnd() {}
function dragEnabled() {}
@@ -1211,14 +1307,15 @@
angular.module('ovTopo')
.factory('TopoForceService',
['$log', 'FnService', 'SvgUtilService', 'IconService', 'ThemeService',
- 'TopoInstService',
+ 'FlashService', 'TopoInstService',
- function (_$log_, _fs_, _sus_, _is_, _ts_, _tis_) {
+ function (_$log_, _fs_, _sus_, _is_, _ts_, _flash_, _tis_) {
$log = _$log_;
fs = _fs_;
sus = _sus_;
is = _is_;
ts = _ts_;
+ flash = _flash_;
tis = _tis_;
icfg = is.iconConfig();
@@ -1270,6 +1367,9 @@
resize: resize,
updateDeviceColors: updateDeviceColors,
+ toggleHosts: toggleHosts,
+ toggleOffline: toggleOffline,
+ cycleDeviceLabels: cycleDeviceLabels,
addDevice: addDevice,
updateDevice: updateDevice,
diff --git a/web/gui/src/main/webapp/app/view/topo/topoInst.js b/web/gui/src/main/webapp/app/view/topo/topoInst.js
index ee5c96c..49e129c 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoInst.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoInst.js
@@ -55,7 +55,6 @@
// ==========================
- // *** ADD INSTANCE ***
function addInstance(data) {
var id = data.id;
@@ -330,7 +329,8 @@
isVisible: function () { return oiBox.isVisible(); },
show: function () { oiBox.show(); },
- hide: function () { oiBox.hide(); }
+ hide: function () { oiBox.hide(); },
+ toggle: function () { oiBox.toggle(); }
};
}]);
}());
diff --git a/web/gui/src/main/webapp/tests/app/fw/layer/panel-spec.js b/web/gui/src/main/webapp/tests/app/fw/layer/panel-spec.js
index 64c54f1..c6c63c3 100644
--- a/web/gui/src/main/webapp/tests/app/fw/layer/panel-spec.js
+++ b/web/gui/src/main/webapp/tests/app/fw/layer/panel-spec.js
@@ -87,7 +87,8 @@
it('should provide an api of panel functions', function () {
var p = ps.createPanel('foo');
expect(fs.areFunctions(p, [
- 'show', 'hide', 'empty', 'append', 'width', 'height', 'isVisible', 'el'
+ 'show', 'hide', 'toggle', 'empty', 'append',
+ 'width', 'height', 'isVisible', 'el'
])).toBeTruthy();
});
diff --git a/web/gui/src/main/webapp/tests/app/fw/svg/svgUtil-spec.js b/web/gui/src/main/webapp/tests/app/fw/svg/svgUtil-spec.js
index e1a2107..69fe750 100644
--- a/web/gui/src/main/webapp/tests/app/fw/svg/svgUtil-spec.js
+++ b/web/gui/src/main/webapp/tests/app/fw/svg/svgUtil-spec.js
@@ -76,7 +76,7 @@
});
it('should provide an alternate (dark) shade of blue for muted', function () {
- expect(sus.cat7().getColor('foo', true, 'dark')).toEqual('#16203A');
+ expect(sus.cat7().getColor('foo', true, 'dark')).toEqual('#304860');
});
it('should iterate across the colors', function () {
diff --git a/web/gui/src/main/webapp/tests/app/view/topo/topoForce-spec.js b/web/gui/src/main/webapp/tests/app/view/topo/topoForce-spec.js
index b85272d..7c65d8c 100644
--- a/web/gui/src/main/webapp/tests/app/view/topo/topoForce-spec.js
+++ b/web/gui/src/main/webapp/tests/app/view/topo/topoForce-spec.js
@@ -35,6 +35,7 @@
it('should define api functions', function () {
expect(fs.areFunctions(tfs, [
'initForce', 'resize', 'updateDeviceColors',
+ 'toggleHosts', 'toggleOffline','cycleDeviceLabels',
'addDevice', 'updateDevice', 'removeDevice',
'addHost', 'updateHost', 'removeHost',
'addLink', 'updateLink', 'removeLink'
diff --git a/web/gui/src/main/webapp/tests/app/view/topo/topoInst-spec.js b/web/gui/src/main/webapp/tests/app/view/topo/topoInst-spec.js
index f54d839..8eefc4a 100644
--- a/web/gui/src/main/webapp/tests/app/view/topo/topoInst-spec.js
+++ b/web/gui/src/main/webapp/tests/app/view/topo/topoInst-spec.js
@@ -36,7 +36,7 @@
expect(fs.areFunctions(tis, [
'initInst', 'destroyInst',
'addInstance', 'updateInstance', 'removeInstance',
- 'isVisible', 'show', 'hide'
+ 'isVisible', 'show', 'hide', 'toggle'
])).toBeTruthy();
});