GUI -- TopoView - Migrated more helper functions to topoModel.js.
Change-Id: I902c3561210c46fd23c6f6f01323d003dacefc19
diff --git a/web/gui/src/main/webapp/app/view/topo/topoEvent.js b/web/gui/src/main/webapp/app/view/topo/topoEvent.js
index bbe3d3a..887fe4a 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoEvent.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoEvent.js
@@ -61,7 +61,7 @@
eh = api[eid];
if (eh) {
- $log.debug(' *EVENT* ', ev.payload);
+ $log.debug(' *EVENT* ', eid, ev.payload);
eh(ev.payload);
} else {
$log.warn('Unknown event (ignored):', ev);
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 e29c6c0..703212f 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoForce.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoForce.js
@@ -70,6 +70,7 @@
revLinkToKey: {}
},
lu = network.lookup, // shorthand
+ rlk = network.revLinkToKey,
deviceLabelIndex = 0, // for device label cycling
hostLabelIndex = 0, // for host label cycling
showHosts = true, // whether hosts are displayed
@@ -134,9 +135,6 @@
d = tms.createDeviceNode(data);
network.nodes.push(d);
lu[id] = d;
-
- $log.debug("Created new device.. ", d.id, d.x, d.y);
-
updateNodes();
fStart();
}
@@ -154,7 +152,7 @@
}
updateNodes();
if (wasOnline !== d.online) {
- findAttachedLinks(d.id).forEach(restyleLinkElement);
+ tms.findAttachedLinks(d.id).forEach(restyleLinkElement);
updateOfflineVisibility(d);
}
} else {
@@ -188,16 +186,10 @@
d = tms.createHostNode(data);
network.nodes.push(d);
lu[id] = d;
-
- $log.debug("Created new host.. ", d.id, d.x, d.y);
-
updateNodes();
lnk = tms.createHostLink(data);
if (lnk) {
-
- $log.debug("Created new host-link.. ", lnk.key);
-
d.linkData = lnk; // cache ref on its host
network.links.push(lnk);
lu[d.ingress] = lnk;
@@ -235,7 +227,7 @@
}
function addLink(data) {
- var result = findLink(data, 'add'),
+ var result = tms.findLink(data, 'add'),
bad = result.badLogic,
d = result.ldata;
@@ -261,7 +253,7 @@
}
function updateLink(data) {
- var result = findLink(data, 'update'),
+ var result = tms.findLink(data, 'update'),
bad = result.badLogic;
if (bad) {
//logicError(bad + ': ' + link.id);
@@ -271,7 +263,7 @@
}
function removeLink(data) {
- var result = findLink(data, 'remove'),
+ var result = tms.findLink(data, 'remove'),
bad = result.badLogic;
if (bad) {
// may have already removed link, if attached to removed device
@@ -286,20 +278,10 @@
function addLinkUpdate(ldata, link) {
// add link event, but we already have the reverse link installed
ldata.fromTarget = link;
- network.revLinkToKey[link.id] = ldata.key;
+ rlk[link.id] = ldata.key;
restyleLinkElement(ldata);
}
- function makeNodeKey(d, what) {
- var port = what + 'Port';
- return d[what] + '/' + d[port];
- }
-
- function makeLinkKey(d, flipped) {
- var one = flipped ? makeNodeKey(d, 'dst') : makeNodeKey(d, 'src'),
- two = flipped ? makeNodeKey(d, 'src') : makeNodeKey(d, 'dst');
- return one + '-' + two;
- }
var widthRatio = 1.4,
linkScale = d3.scale.linear()
@@ -329,113 +311,7 @@
.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),
- link = lu[key],
- linkRev = lu[keyrev],
- result = {},
- ldata = link || linkRev,
- rawLink;
-
- if (op === 'add') {
- if (link) {
- // trying to add a link that we already know about
- result.ldata = link;
- result.badLogic = 'addLink: link already added';
-
- } else if (linkRev) {
- // we found the reverse of the link to be added
- result.ldata = linkRev;
- if (linkRev.fromTarget) {
- result.badLogic = 'addLink: link already added';
- }
- }
- } else if (op === 'update') {
- if (!ldata) {
- result.badLogic = 'updateLink: link not found';
- } else {
- rawLink = link ? ldata.fromSource : ldata.fromTarget;
- result.updateWith = function (data) {
- angular.extend(rawLink, data);
- restyleLinkElement(ldata);
- }
- }
- } else if (op === 'remove') {
- if (!ldata) {
- result.badLogic = 'removeLink: link not found';
- } else {
- rawLink = link ? ldata.fromSource : ldata.fromTarget;
-
- if (!rawLink) {
- result.badLogic = 'removeLink: link not found';
-
- } else {
- result.removeRawLink = function () {
- if (link) {
- // remove fromSource
- ldata.fromSource = null;
- if (ldata.fromTarget) {
- // promote target into source position
- ldata.fromSource = ldata.fromTarget;
- ldata.fromTarget = null;
- ldata.key = keyrev;
- delete network.lookup[key];
- network.lookup[keyrev] = ldata;
- delete network.revLinkToKey[keyrev];
- }
- } else {
- // remove fromTarget
- ldata.fromTarget = null;
- delete network.revLinkToKey[keyrev];
- }
- if (ldata.fromSource) {
- restyleLinkElement(ldata);
- } else {
- removeLinkElement(ldata);
- }
- }
- }
- }
- }
- return result;
- }
-
- function findDevices(offlineOnly) {
- var a = [];
- network.nodes.forEach(function (d) {
- if (d.class === 'device' && !(offlineOnly && d.online)) {
- a.push(d);
- }
- });
- return a;
- }
-
- function findAttachedHosts(devId) {
- var hosts = [];
- network.nodes.forEach(function (d) {
- if (d.class === 'host' && d.cp.device === devId) {
- hosts.push(d);
- }
- });
- return hosts;
- }
-
- function findAttachedLinks(devId) {
- var links = [];
- network.links.forEach(function (d) {
- if (d.source.id === devId || d.target.id === devId) {
- links.push(d);
- }
- });
- return links;
- }
function removeLinkElement(d) {
var idx = fs.find(d.key, network.links, 'key'),
@@ -475,8 +351,8 @@
function removeDeviceElement(d) {
var id = d.id;
// first, remove associated hosts and links..
- findAttachedHosts(id).forEach(removeHostElement);
- findAttachedLinks(id).forEach(removeLinkElement);
+ tms.findAttachedHosts(id).forEach(removeHostElement);
+ tms.findAttachedLinks(id).forEach(removeLinkElement);
// remove from lookup cache
delete lu[id];
@@ -485,7 +361,7 @@
network.nodes.splice(idx, 1);
if (!network.nodes.length) {
- xlink.showNoDevs(true);
+ uplink.showNoDevs(true);
}
// remove from SVG
@@ -502,11 +378,11 @@
function updDev(d, show) {
sus.makeVisible(d.el, show);
- findAttachedLinks(d.id).forEach(function (link) {
+ tms.findAttachedLinks(d.id).forEach(function (link) {
b = show && ((link.type() !== 'hostLink') || showHosts);
sus.makeVisible(link.el, b);
});
- findAttachedHosts(d.id).forEach(function (host) {
+ tms.findAttachedHosts(d.id).forEach(function (host) {
b = show && showHosts;
sus.makeVisible(host.el, b);
});
@@ -517,7 +393,7 @@
updDev(dev, dev.online || showOffline);
} else {
// updating all offline devices
- findDevices(true).forEach(function (d) {
+ tms.findDevices(true).forEach(function (d) {
updDev(d, showOffline);
});
}
@@ -532,12 +408,7 @@
// attach the x, y, longitude, latitude...
if (!clearPos) {
ll = tms.lngLatFromCoord([d.x, d.y]);
- metaUi = {
- x: d.x,
- y: d.y,
- lng: ll[0],
- lat: ll[1]
- };
+ metaUi = {x: d.x, y: d.y, lng: ll[0], lat: ll[1]};
}
d.metaUi = metaUi;
uplink.sendEvent('updateMeta', {
@@ -691,7 +562,7 @@
function cycleDeviceLabels() {
deviceLabelIndex = (deviceLabelIndex+1) % 3;
- findDevices().forEach(function (d) {
+ tms.findDevices().forEach(function (d) {
updateDeviceLabel(d);
});
}
@@ -1107,7 +978,7 @@
},
linkLabelAttr: {
transform: function (d) {
- var lnk = findLinkById(d.key);
+ var lnk = tms.findLinkById(d.key);
if (lnk) {
return transformLabel({
x1: lnk.source.x,
@@ -1223,6 +1094,15 @@
// ==========================
// Module definition
+ function mkModelApi(uplink) {
+ return {
+ projection: uplink.projection,
+ network: network,
+ restyleLinkElement: restyleLinkElement,
+ removeLinkElement: removeLinkElement
+ };
+ }
+
angular.module('ovTopo')
.factory('TopoForceService',
['$log', 'FnService', 'SvgUtilService', 'IconService', 'ThemeService',
@@ -1241,7 +1121,7 @@
icfg = is.iconConfig();
// forceG is the SVG group to display the force layout in
- // xlink is the cross-link api from the main topo source file
+ // uplink is the api from the main topo source file
// dim is the initial dimensions of the SVG as [w,h]
// opts are, well, optional :)
function initForce(forceG, _uplink_, _dim_, opts) {
@@ -1250,10 +1130,7 @@
$log.debug('initForce().. dim = ' + dim);
- tms.initModel({
- projection: uplink.projection,
- lookup: network.lookup
- }, dim);
+ tms.initModel(mkModelApi(uplink), dim);
settings = angular.extend({}, defaultSettings, opts);
diff --git a/web/gui/src/main/webapp/app/view/topo/topoModel.js b/web/gui/src/main/webapp/app/view/topo/topoModel.js
index 015fbdd..4754f60 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoModel.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoModel.js
@@ -26,6 +26,15 @@
// injected refs
var $log, fs, rnd, api;
+ // shorthand
+ var lu, rlk, nodes, links;
+
+ // api:
+ // projection: func()
+ // network {...}
+ // restyleLinkElement: func(ldata)
+ // removeLinkElement: func(ldata)
+
var dim; // dimensions of layout, as [w,h]
// configuration 'constants'
@@ -95,7 +104,7 @@
}
function getDevice(cp) {
- var d = api.lookup[cp.device];
+ var d = lu[cp.device];
return d || rand();
}
@@ -194,8 +203,8 @@
function linkEndPoints(srcId, dstId) {
- var srcNode = api.lookup[srcId],
- dstNode = api.lookup[dstId],
+ var srcNode = lu[srcId],
+ dstNode = lu[dstId],
sMiss = !srcNode ? missMsg('src', srcId) : '',
dMiss = !dstNode ? missMsg('dst', dstId) : '';
@@ -218,6 +227,127 @@
return '\n[' + what + '] "' + id + '" missing';
}
+
+ function makeNodeKey(d, what) {
+ var port = what + 'Port';
+ return d[what] + '/' + d[port];
+ }
+
+ function makeLinkKey(d, flipped) {
+ var one = flipped ? makeNodeKey(d, 'dst') : makeNodeKey(d, 'src'),
+ two = flipped ? makeNodeKey(d, 'src') : makeNodeKey(d, 'dst');
+ return one + '-' + two;
+ }
+
+ function findLinkById(id) {
+ // check to see if this is a reverse lookup, else default to given id
+ var key = rlk[id] || id;
+ return key && lu[key];
+ }
+
+ function findLink(linkData, op) {
+ var key = makeLinkKey(linkData),
+ keyrev = makeLinkKey(linkData, 1),
+ link = lu[key],
+ linkRev = lu[keyrev],
+ result = {},
+ ldata = link || linkRev,
+ rawLink;
+
+ if (op === 'add') {
+ if (link) {
+ // trying to add a link that we already know about
+ result.ldata = link;
+ result.badLogic = 'addLink: link already added';
+
+ } else if (linkRev) {
+ // we found the reverse of the link to be added
+ result.ldata = linkRev;
+ if (linkRev.fromTarget) {
+ result.badLogic = 'addLink: link already added';
+ }
+ }
+ } else if (op === 'update') {
+ if (!ldata) {
+ result.badLogic = 'updateLink: link not found';
+ } else {
+ rawLink = link ? ldata.fromSource : ldata.fromTarget;
+ result.updateWith = function (data) {
+ angular.extend(rawLink, data);
+ api.restyleLinkElement(ldata);
+ }
+ }
+ } else if (op === 'remove') {
+ if (!ldata) {
+ result.badLogic = 'removeLink: link not found';
+ } else {
+ rawLink = link ? ldata.fromSource : ldata.fromTarget;
+
+ if (!rawLink) {
+ result.badLogic = 'removeLink: link not found';
+
+ } else {
+ result.removeRawLink = function () {
+ if (link) {
+ // remove fromSource
+ ldata.fromSource = null;
+ if (ldata.fromTarget) {
+ // promote target into source position
+ ldata.fromSource = ldata.fromTarget;
+ ldata.fromTarget = null;
+ ldata.key = keyrev;
+ delete lu[key];
+ lu[keyrev] = ldata;
+ delete rlk[keyrev];
+ }
+ } else {
+ // remove fromTarget
+ ldata.fromTarget = null;
+ delete rlk[keyrev];
+ }
+ if (ldata.fromSource) {
+ api.restyleLinkElement(ldata);
+ } else {
+ api.removeLinkElement(ldata);
+ }
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ function findDevices(offlineOnly) {
+ var a = [];
+ nodes.forEach(function (d) {
+ if (d.class === 'device' && !(offlineOnly && d.online)) {
+ a.push(d);
+ }
+ });
+ return a;
+ }
+
+ function findAttachedHosts(devId) {
+ var hosts = [];
+ nodes.forEach(function (d) {
+ if (d.class === 'host' && d.cp.device === devId) {
+ hosts.push(d);
+ }
+ });
+ return hosts;
+ }
+
+ function findAttachedLinks(devId) {
+ var links = [];
+ links.forEach(function (d) {
+ if (d.source.id === devId || d.target.id === devId) {
+ links.push(d);
+ }
+ });
+ return links;
+ }
+
+
// ==========================
// Module definition
@@ -233,6 +363,10 @@
function initModel(_api_, _dim_) {
api = _api_;
dim = _dim_;
+ lu = api.network.lookup;
+ rlk = api.network.revLinkToKey;
+ nodes = api.network.nodes;
+ links = api.network.links;
}
function newDim(_dim_) {
@@ -250,6 +384,11 @@
createLink: createLink,
coordFromLngLat: coordFromLngLat,
lngLatFromCoord: lngLatFromCoord,
+ findLink: findLink,
+ findLinkById: findLinkById,
+ findDevices: findDevices,
+ findAttachedHosts: findAttachedHosts,
+ findAttachedLinks: findAttachedLinks
}
}]);
}());
diff --git a/web/gui/src/main/webapp/tests/app/view/topo/topoModel-spec.js b/web/gui/src/main/webapp/tests/app/view/topo/topoModel-spec.js
index a0d488b..67291f3 100644
--- a/web/gui/src/main/webapp/tests/app/view/topo/topoModel-spec.js
+++ b/web/gui/src/main/webapp/tests/app/view/topo/topoModel-spec.js
@@ -45,23 +45,22 @@
return [xy[0] + 2000, xy[1] + 3000];
};
- // our test device lookup
- var lu = {
- dev1: {
+ // our test devices and hosts:
+ var dev1 = {
'class': 'device',
id: 'dev1',
x: 17,
y: 27,
online: true
},
- dev2: {
+ dev2 = {
'class': 'device',
id: 'dev2',
x: 18,
y: 28,
online: true
},
- host1: {
+ host1 = {
'class': 'host',
id: 'host1',
x: 23,
@@ -72,7 +71,7 @@
},
ingress: 'dev1/7-host1'
},
- host2: {
+ host2 = {
'class': 'host',
id: 'host2',
x: 24,
@@ -82,13 +81,20 @@
port: 0
},
ingress: 'dev0/0-host2'
- }
- };
+ };
+
// our test api
var api = {
projection: function () { return mockProjection; },
- lookup: lu
+ network: {
+ nodes: [dev1, dev2, host1, host2],
+ links: [],
+ lookup: {dev1: dev1, dev2: dev2, host1: host1, host2: host2},
+ revLinkToKey: {}
+ },
+ restyleLinkElement: function () {},
+ removeLinkElement: function () {}
};
// our test dimensions and well known locations..
@@ -204,7 +210,9 @@
'initModel', 'newDim',
'positionNode', 'createDeviceNode', 'createHostNode',
'createHostLink', 'createLink',
- 'coordFromLngLat', 'lngLatFromCoord'
+ 'coordFromLngLat', 'lngLatFromCoord',
+ 'findLink', 'findLinkById', 'findDevices',
+ 'findAttachedHosts', 'findAttachedLinks'
])).toBeTruthy();
});
@@ -348,9 +356,9 @@
// === unit tests for createHostLink()
it('should create a basic host link', function () {
- var link = tms.createHostLink(lu.host1);
- expect(link.source).toEqual(lu.host1);
- expect(link.target).toEqual(lu.dev1);
+ var link = tms.createHostLink(host1);
+ expect(link.source).toEqual(host1);
+ expect(link.target).toEqual(dev1);
expect(link).toHaveEndPoints(host1Loc, dev1Loc);
expect(link.key).toEqual('dev1/7-host1');
expect(link.class).toEqual('link');
@@ -361,7 +369,7 @@
it('should return null for failed endpoint lookup', function () {
spyOn($log, 'error');
- var link = tms.createHostLink(lu.host2);
+ var link = tms.createHostLink(host2);
expect(link).toBeNull();
expect($log.error).toHaveBeenCalledWith(
'Node(s) not on map for link:\n[dst] "dev0" missing'
@@ -389,8 +397,8 @@
linkWidth: 1.5
},
link = tms.createLink(linkData);
- expect(link.source).toEqual(lu.dev1);
- expect(link.target).toEqual(lu.dev2);
+ expect(link.source).toEqual(dev1);
+ expect(link.target).toEqual(dev2);
expect(link).toHaveEndPoints(dev1Loc, dev2Loc);
expect(link.key).toEqual('baz');
expect(link.class).toEqual('link');
@@ -400,4 +408,5 @@
expect(link.linkWidth()).toEqual(1.5);
});
+ // TODO: more unit tests for additional functions....
});
diff --git a/web/gui/src/test/_karma/ev/simple/ev_17_removeDevice_08.json b/web/gui/src/test/_karma/ev/simple/ev_17_removeDevice_08.json
new file mode 100644
index 0000000..15e711d
--- /dev/null
+++ b/web/gui/src/test/_karma/ev/simple/ev_17_removeDevice_08.json
@@ -0,0 +1,23 @@
+{
+ "event": "removeDevice",
+ "payload": {
+ "id": "of:0000ffffffff0008",
+ "type": "switch",
+ "online": false,
+ "master": "myInstA",
+ "location": {
+ "type": "latlng",
+ "lat": 37.7833,
+ "lng": -122.4167
+ },
+ "labels": [
+ "",
+ "sw-8",
+ "0000ffffffff0008"
+ ],
+ "metaUi": {
+ "x": 520,
+ "y": 350
+ }
+ }
+}