Simon Hunt

GUI -- TopoView - Reimplemented 'traffic' related functionality.

Change-Id: I86d16324e4ce2cd2e0eb8d8f48f72804d7ce101f
......@@ -86,6 +86,7 @@
<script src="view/topo/topoModel.js"></script>
<script src="view/topo/topoPanel.js"></script>
<script src="view/topo/topoSelect.js"></script>
<script src="view/topo/topoTraffic.js"></script>
<script src="view/device/device.js"></script>
<!-- TODO: inject javascript refs server-side -->
......
......@@ -28,7 +28,7 @@
];
// references to injected services etc.
var $log, fs, ks, zs, gs, ms, sus, tes, tfs, tps, tis, tss;
var $log, fs, ks, zs, gs, ms, sus, tes, tfs, tps, tis, tss, tts;
// DOM elements
var ovtopo, svg, defs, zoomLayer, mapG, forceG, noDevsLayer;
......@@ -57,12 +57,12 @@
U: [tfs.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: [tts.showRelatedIntentsAction, 'Show all related intents'],
rightArrow: [tts.showNextIntentAction, 'Show next related intent'],
leftArrow: [tts.showPrevIntentAction, 'Show previous related intent'],
W: [tts.showSelectedIntentTrafficAction, 'Monitor traffic of selected intent'],
A: [tts.showAllTrafficAction, 'Monitor all traffic'],
F: [tts.showDeviceLinkFlowsAction, 'Show device link flows'],
//E: [equalizeMasters, 'Equalize mastership roles'],
......@@ -222,11 +222,11 @@
'FnService', 'MastService', 'KeyService', 'ZoomService',
'GlyphService', 'MapService', 'SvgUtilService',
'TopoEventService', 'TopoForceService', 'TopoPanelService',
'TopoInstService', 'TopoSelectService',
'TopoInstService', 'TopoSelectService', 'TopoTrafficService',
function ($scope, _$log_, $loc, $timeout, _fs_, mast,
_ks_, _zs_, _gs_, _ms_, _sus_,
_tes_, _tfs_, _tps_, _tis_, _tss_) {
_tes_, _tfs_, _tps_, _tis_, _tss_, _tts_) {
var self = this,
projection,
dim,
......@@ -249,6 +249,7 @@
tps = _tps_;
tis = _tis_;
tss = _tss_;
tts = _tts_;
self.notifyResize = function () {
svgResized(fs.windowSize(mast.mastHeight()));
......
......@@ -27,7 +27,7 @@
'use strict';
// injected refs
var $log, wss, wes, tps, tis, tfs, tss;
var $log, wss, wes, tps, tis, tfs, tss, tts;
// internal state
var wsock, evApis;
......@@ -40,6 +40,8 @@
showDetails: tss,
showTraffic: tts,
addInstance: tis,
updateInstance: tis,
removeInstance: tis,
......@@ -53,8 +55,6 @@
addLink: tfs,
updateLink: tfs,
removeLink: tfs
// TODO: add remaining event api vectors
};
}
......@@ -106,9 +106,10 @@
.factory('TopoEventService',
['$log', '$location', 'WebSocketService', 'WsEventService',
'TopoPanelService', 'TopoInstService', 'TopoForceService',
'TopoSelectService',
'TopoSelectService', 'TopoTrafficService',
function (_$log_, $loc, _wss_, _wes_, _tps_, _tis_, _tfs_, _tss_) {
function (_$log_, $loc, _wss_, _wes_,
_tps_, _tis_, _tfs_, _tss_, _tts_) {
$log = _$log_;
wss = _wss_;
wes = _wes_;
......@@ -116,6 +117,7 @@
tis = _tis_;
tfs = _tfs_;
tss = _tss_;
tts = _tts_;
bindApis();
......
......@@ -23,7 +23,7 @@
'use strict';
// injected refs
var $log, fs, sus, is, ts, flash, tis, tms, tss, icfg, uplink;
var $log, fs, sus, is, ts, flash, tis, tms, tss, tts, icfg, uplink;
// configuration
var labelConfig = {
......@@ -996,6 +996,21 @@
return true;
}
// ==========================
// function entry points for traffic module
var allTrafficClasses = 'primary secondary animated optical';
function clearLinkTrafficStyle() {
link.style('stroke-width', null)
.classed(allTrafficClasses, false);
}
function removeLinkLabels() {
network.links.forEach(function (d) {
d.label = '';
});
}
// ==========================
// Module definition
......@@ -1018,13 +1033,27 @@
};
}
function mkTrafficApi(uplink) {
return {
clearLinkTrafficStyle: clearLinkTrafficStyle,
removeLinkLabels: removeLinkLabels,
updateLinks: updateLinks,
findLinkById: tms.findLinkById,
hovered: tss.hovered,
validateSelectionContext: tss.validateSelectionContext,
selectOrder: tss.selectOrder,
sendEvent: uplink.sendEvent
}
}
angular.module('ovTopo')
.factory('TopoForceService',
['$log', 'FnService', 'SvgUtilService', 'IconService', 'ThemeService',
'FlashService', 'TopoInstService', 'TopoModelService',
'TopoSelectService',
'TopoSelectService', 'TopoTrafficService',
function (_$log_, _fs_, _sus_, _is_, _ts_, _flash_, _tis_, _tms_, _tss_) {
function (_$log_, _fs_, _sus_, _is_, _ts_, _flash_,
_tis_, _tms_, _tss_, _tts_) {
$log = _$log_;
fs = _fs_;
sus = _sus_;
......@@ -1034,6 +1063,7 @@
tis = _tis_;
tms = _tms_;
tss = _tss_;
tts = _tts_;
icfg = is.iconConfig();
......@@ -1049,6 +1079,7 @@
tms.initModel(mkModelApi(uplink), dim);
tss.initSelect(mkSelectApi(uplink));
tts.initTraffic(mkTrafficApi(uplink));
settings = angular.extend({}, defaultSettings, opts);
......@@ -1083,7 +1114,9 @@
}
function destroyForce() {
tts.destroyTraffic();
tss.destroySelect();
tms.destroyModel();
}
return {
......
......@@ -376,9 +376,12 @@
dim = _dim_;
}
function destroyModel() { }
return {
initModel: initModel,
newDim: newDim,
destroyModel: destroyModel,
positionNode: positionNode,
createDeviceNode: createDeviceNode,
......
......@@ -23,7 +23,7 @@
'use strict';
// injected refs
var $log, fs, flash, tps;
var $log, fs, flash, tps, tts;
// api to topoForce
var api;
......@@ -65,7 +65,7 @@
$log.debug("MouseOver()...", m);
if (hovered != m) {
hovered = m;
requestTrafficForMode();
tts.requestTrafficForMode();
}
}
}
......@@ -74,7 +74,7 @@
if (!m.dragStarted) {
if (hovered) {
hovered = null;
requestTrafficForMode();
tts.requestTrafficForMode();
}
$log.debug("MouseOut()...", m);
}
......@@ -118,8 +118,6 @@
n.classed('selected', true);
api.updateDeviceColors(obj);
updateDetail();
debugSel();
}
function deselectObject(id) {
......@@ -130,8 +128,6 @@
fs.removeFromArray(id, selectOrder);
api.updateDeviceColors(obj.obj);
}
debugSel();
}
function deselectAll() {
......@@ -141,12 +137,6 @@
selectOrder = [];
api.updateDeviceColors();
updateDetail();
debugSel();
}
function debugSel() {
$log.debug(' ..... Selected now >> ', selectOrder);
}
// === -----------------------------------------------------
......@@ -175,14 +165,14 @@
function emptySelect() {
haveDetails = false;
tps.hideDetailPanel();
cancelTraffic();
tts.cancelTraffic();
}
function singleSelect() {
// NOTE: detail is shown from 'showDetails' event callback
requestDetails();
cancelTraffic();
requestTrafficForMode();
tts.cancelTraffic();
tts.requestTrafficForMode();
}
function multiSelect() {
......@@ -192,17 +182,17 @@
tps.displayMulti(selectOrder);
// always add the 'show traffic' action
tps.addAction('Show Related Traffic', showRelatedIntentsAction);
tps.addAction('Show Related Traffic', tts.showRelatedIntentsAction);
// add other actions, based on what is selected...
if (nSel() === 2 && allSelectionsClass('host')) {
tps.addAction('Create Host-to-Host Flow', addHostIntentAction);
tps.addAction('Create Host-to-Host Flow', tts.addHostIntentAction);
} else if (nSel() >= 2 && allSelectionsClass('host')) {
tps.addAction('Create Multi-Source Flow', addMultiSourceIntentAction);
tps.addAction('Create Multi-Source Flow', tts.addMultiSourceIntentAction);
}
cancelTraffic();
requestTrafficForMode();
tts.cancelTraffic();
tts.requestTrafficForMode();
}
......@@ -216,11 +206,11 @@
tps.displaySingle(data);
// always add the 'show traffic' action
tps.addAction('Show Related Traffic', showRelatedIntentsAction);
tps.addAction('Show Related Traffic', tts.showRelatedIntentsAction);
// add other actions, based on what is selected...
if (data.type === 'switch') {
tps.addAction('Show Device Flows', showDeviceLinkFlowsAction);
tps.addAction('Show Device Flows', tts.showDeviceLinkFlowsAction);
}
// only show the details panel if the user hasn't "hidden" it
......@@ -242,47 +232,28 @@
}
}
// === -----------------------------------------------------
// TODO: migrate these to topoTraffic.js
function cancelTraffic() {
$log.debug('TODO: cancelTraffic');
}
function requestTrafficForMode() {
$log.debug('TODO: requestTrafficForMode');
}
function showRelatedIntentsAction () {
$log.debug('TODO: showRelatedIntentsAction');
}
function addHostIntentAction () {
$log.debug('TODO: addHostIntentAction');
}
function addMultiSourceIntentAction () {
$log.debug('TODO: addMultiSourceIntentAction');
function validateSelectionContext() {
if (!hovered && !nSel()) {
tts.cancelTraffic();
return false;
}
function showDeviceLinkFlowsAction () {
$log.debug('TODO: showDeviceLinkFlowsAction');
return true;
}
// === -----------------------------------------------------
// === MODULE DEFINITION ===
angular.module('ovTopo')
.factory('TopoSelectService',
['$log', 'FnService', 'FlashService', 'TopoPanelService',
'TopoTrafficService',
function (_$log_, _fs_, _flash_, _tps_) {
function (_$log_, _fs_, _flash_, _tps_, _tts_) {
$log = _$log_;
fs = _fs_;
flash = _flash_;
tps = _tps_;
tts = _tts_;
function initSelect(_api_) {
api = _api_;
......@@ -302,8 +273,11 @@
selectObject: selectObject,
deselectObject: deselectObject,
deselectAll: deselectAll,
hovered: function () { return hovered; },
haveDetails: function () { return haveDetails; }
haveDetails: function () { return haveDetails; },
selectOrder: function () { return selectOrder; },
validateSelectionContext: validateSelectionContext
};
}]);
}());
......
/*
* Copyright 2015 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 Traffic Module.
Defines behavior for viewing different traffic modes.
*/
(function () {
'use strict';
// injected refs
var $log, fs, flash;
// api to topoForce
var api;
/*
clearLinkTrafficStyle()
removeLinkLabels()
updateLinks()
findLinkById( id )
hovered()
validateSelectionContext()
sendEvent( type, {payload} )
*/
// constants
var hoverModeNone = 0,
hoverModeAll = 1,
hoverModeFlows = 2,
hoverModeIntents = 3;
// internal state
var hoverMode = hoverModeNone;
// === -----------------------------------------------------
// Event Handlers
function showTraffic(data) {
var paths = data.paths;
api.clearLinkTrafficStyle();
api.removeLinkLabels();
// Now highlight all links in the paths payload, and attach
// labels to them, if they are defined.
paths.forEach(function (p) {
var n = p.links.length,
i, ldata;
for (i=0; i<n; i++) {
ldata = api.findLinkById(p.links[i]);
if (ldata && ldata.el) {
ldata.el.classed(p.class, true);
ldata.label = p.labels[i];
}
}
});
api.updateLinks();
}
// === -----------------------------------------------------
// Helper functions
function requestDeviceLinkFlows() {
var hov = api.hovered();
function hoverValid() {
return hoverMode === hoverModeFlows &&
hov && (hov.class === 'device');
}
if (api.validateSelectionContext()) {
api.sendEvent('requestDeviceLinkFlows', {
ids: api.selectOrder(),
hover: hoverValid() ? hov.id : ''
});
}
}
function requestRelatedIntents() {
var hov = api.hovered();
function hoverValid() {
return hoverMode === hoverModeIntents &&
hov && (hov.class === 'host' || hov.class === 'device');
}
if (api.validateSelectionContext()) {
api.sendEvent('requestRelatedIntents', {
ids: api.selectOrder(),
hover: hoverValid() ? hov.id : ''
});
}
}
// === -----------------------------------------------------
// Traffic requests
function cancelTraffic() {
api.sendEvent('cancelTraffic');
}
// invoked in response to change in selection and/or mouseover/out:
function requestTrafficForMode() {
if (hoverMode === hoverModeFlows) {
requestDeviceLinkFlows();
} else if (hoverMode === hoverModeIntents) {
requestRelatedIntents();
}
}
// === -----------------------------
// keystroke commands
// keystroke-right-arrow (see topo.js)
function showNextIntentAction() {
hoverMode = hoverModeNone;
api.sendEvent('requestNextRelatedIntent');
flash.flash('>');
}
// keystroke-left-arrow (see topo.js)
function showPrevIntentAction() {
hoverMode = hoverModeNone;
api.sendEvent('requestPrevRelatedIntent');
flash.flash('<');
}
// keystroke-W (see topo.js)
function showSelectedIntentTrafficAction() {
hoverMode = hoverModeNone;
api.sendEvent('requestSelectedIntentTraffic');
flash.flash('Traffic on Selected Path');
}
// keystroke-A (see topo.js)
function showAllTrafficAction() {
hoverMode = hoverModeAll;
api.sendEvent('requestAllTraffic');
flash.flash('All Traffic');
}
// === -----------------------------
// action buttons on detail panel
// also, keystroke-V (see topo.js)
function showRelatedIntentsAction () {
hoverMode = hoverModeIntents;
requestRelatedIntents();
flash.flash('Related Paths');
}
function addHostIntentAction () {
var so = api.selectOrder();
api.sendEvent('addHostIntent', {
one: so[0],
two: so[1],
ids: so
});
flash.flash('Host-to-Host flow added');
}
function addMultiSourceIntentAction () {
var so = api.selectOrder();
api.sendEvent('addMultiSourceIntent', {
src: so.slice(0, so.length - 1),
dst: so[so.length - 1],
ids: so
});
flash.flash('Multi-Source flow added');
}
// also, keystroke-F (see topo.js)
function showDeviceLinkFlowsAction () {
hoverMode = hoverModeFlows;
requestDeviceLinkFlows();
flash.flash('Device Flows');
}
// === -----------------------------------------------------
// === MODULE DEFINITION ===
angular.module('ovTopo')
.factory('TopoTrafficService',
['$log', 'FnService', 'FlashService',
function (_$log_, _fs_, _flash_) {
$log = _$log_;
fs = _fs_;
flash = _flash_;
function initTraffic(_api_) {
api = _api_;
}
function destroyTraffic() { }
return {
initTraffic: initTraffic,
destroyTraffic: destroyTraffic,
showTraffic: showTraffic,
cancelTraffic: cancelTraffic,
requestTrafficForMode: requestTrafficForMode,
showRelatedIntentsAction: showRelatedIntentsAction,
addHostIntentAction: addHostIntentAction,
addMultiSourceIntentAction: addMultiSourceIntentAction,
showDeviceLinkFlowsAction: showDeviceLinkFlowsAction,
showNextIntentAction: showNextIntentAction,
showPrevIntentAction: showPrevIntentAction,
showSelectedIntentTrafficAction: showSelectedIntentTrafficAction,
showAllTrafficAction: showAllTrafficAction
};
}]);
}());
......@@ -207,7 +207,7 @@ describe('factory: view/topo/topoModel.js', function() {
it('should define api functions', function () {
expect(fs.areFunctions(tms, [
'initModel', 'newDim',
'initModel', 'newDim', 'destroyModel',
'positionNode', 'createDeviceNode', 'createHostNode',
'createHostLink', 'createLink',
'coordFromLngLat', 'lngLatFromCoord',
......
......@@ -36,7 +36,8 @@ describe('factory: view/topo/topoSelect.js', function() {
expect(fs.areFunctions(tss, [
'initSelect', 'destroySelect', 'showDetails', 'toggleDetails',
'nodeMouseOver', 'nodeMouseOut', 'selectObject', 'deselectObject',
'deselectAll', 'hovered', 'haveDetails'
'deselectAll', 'hovered', 'haveDetails', 'selectOrder',
'validateSelectionContext'
])).toBeTruthy();
});
......
/*
* Copyright 2015 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 -- Topo View -- Topo Traffic Service - Unit Tests
*/
describe('factory: view/topo/topoTraffic.js', function() {
var $log, fs, tts;
beforeEach(module('ovTopo', 'onosUtil', 'onosLayer'));
beforeEach(inject(function (_$log_, FnService, TopoTrafficService) {
$log = _$log_;
fs = FnService;
tts = TopoTrafficService;
}));
it('should define TopoTrafficService', function () {
expect(tts).toBeDefined();
});
it('should define api functions', function () {
expect(fs.areFunctions(tts, [
'initTraffic', 'destroyTraffic', 'showTraffic',
'cancelTraffic', 'requestTrafficForMode',
'showRelatedIntentsAction', 'addHostIntentAction',
'addMultiSourceIntentAction', 'showDeviceLinkFlowsAction',
'showNextIntentAction', 'showPrevIntentAction',
'showSelectedIntentTrafficAction', 'showAllTrafficAction'
])).toBeTruthy();
});
// TODO: more tests...
});